From af3590c668fccadc2f1a4bfb88d4047fcfe4a583 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 31 Jan 2023 10:00:22 -0500 Subject: [PATCH 01/79] change call for insertRasterResult to allow remote call --- ispybLib.py | 3 ++- lsdcGui.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ispybLib.py b/ispybLib.py index bc0fd2ed..b66ec189 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -387,13 +387,14 @@ def createDataCollection(directory, filePrefix, jpegImageFilename, params, reque # if request_type == 'screening': # params['overlap'] = 89.0 -def insertRasterResult(request,visitName): +def insertRasterResult(request_id,visitName): return try: sessionid = core.retrieve_visit_id(visitName) except ISPyBNoResultException as e: logger.error("insertRasterResult - caught ISPyBNoResultException: '%s'. make sure visit name is in the format mx999999-1234. NOT HAVING MX IN FRONT IS A SIGN OF PROBLEMS - try newVisit() in that case." % e) return + request = db_lib.getRequestByID(request_id) sample = request['sample'] # this needs to be created and linked to a DC group #result_obj = result['result_obj'] this doesn't appear to be used -DK request_obj = request['request_obj'] diff --git a/lsdcGui.py b/lsdcGui.py index 52167feb..68f8f3ea 100644 --- a/lsdcGui.py +++ b/lsdcGui.py @@ -4090,7 +4090,7 @@ def takeRasterSnapshot(self,rasterReq): self.saveVidSnapshotCB("Raster Result from sample " + str(rasterReq["request_obj"]["file_prefix"]),useOlog=False,reqID=rasterReq["uid"],rasterHeatJpeg=jpegImageFilename) self.saveVidSnapshotCB("Raster Result from sample " + str(rasterReq["request_obj"]["file_prefix"]),useOlog=False,reqID=rasterReq["uid"],rasterHeatJpeg=jpegImageFilename) try: - ispybLib.insertRasterResult(rasterReq,visitName) + ispybLib.insertRasterResult(rasterReq["uid"],visitName) except Exception as e: logger.error(f'Exception while writing raster result: {e}') From 54517851fb4f746e28d3febf254ea85adaf02002 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 31 Jan 2023 10:01:38 -0500 Subject: [PATCH 02/79] send insertRasterResult() call to server --- lsdcGui.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lsdcGui.py b/lsdcGui.py index 68f8f3ea..bb99ad55 100644 --- a/lsdcGui.py +++ b/lsdcGui.py @@ -4089,10 +4089,7 @@ def takeRasterSnapshot(self,rasterReq): logger.info("saving raster snapshot") self.saveVidSnapshotCB("Raster Result from sample " + str(rasterReq["request_obj"]["file_prefix"]),useOlog=False,reqID=rasterReq["uid"],rasterHeatJpeg=jpegImageFilename) self.saveVidSnapshotCB("Raster Result from sample " + str(rasterReq["request_obj"]["file_prefix"]),useOlog=False,reqID=rasterReq["uid"],rasterHeatJpeg=jpegImageFilename) - try: - ispybLib.insertRasterResult(rasterReq["uid"],visitName) - except Exception as e: - logger.error(f'Exception while writing raster result: {e}') + self.send_to_server(f"ispybLib.insertRasterResult({rasterReq['uid']},{visitName})") From 698d8b3ed6b87ee9377777a53987683a64dc0ee1 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 31 Jan 2023 10:02:17 -0500 Subject: [PATCH 03/79] remove ispybLib requirement from GUI --- lsdcGui.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lsdcGui.py b/lsdcGui.py index bb99ad55..f9263b53 100644 --- a/lsdcGui.py +++ b/lsdcGui.py @@ -70,10 +70,6 @@ def filter(self, record): #handler2.setFormatter(myformat) logger.addHandler(handler1) #logger.addHandler(handler2) -try: - import ispybLib -except Exception as e: - logger.error("lsdcGui: ISPYB import error, %s" % e) import raddoseLib global sampleNameDict From 0b7f3868e9d2810a99ad778a40684aa9cc38cffd Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Wed, 1 Feb 2023 09:30:44 -0500 Subject: [PATCH 04/79] fix string output so literals are surrounded with single quotes --- lsdcGui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsdcGui.py b/lsdcGui.py index f9263b53..6ad53117 100644 --- a/lsdcGui.py +++ b/lsdcGui.py @@ -4085,7 +4085,7 @@ def takeRasterSnapshot(self,rasterReq): logger.info("saving raster snapshot") self.saveVidSnapshotCB("Raster Result from sample " + str(rasterReq["request_obj"]["file_prefix"]),useOlog=False,reqID=rasterReq["uid"],rasterHeatJpeg=jpegImageFilename) self.saveVidSnapshotCB("Raster Result from sample " + str(rasterReq["request_obj"]["file_prefix"]),useOlog=False,reqID=rasterReq["uid"],rasterHeatJpeg=jpegImageFilename) - self.send_to_server(f"ispybLib.insertRasterResult({rasterReq['uid']},{visitName})") + self.send_to_server(f"ispybLib.insertRasterResult('{rasterReq['uid']}','{visitName}')") From f23c86ff3975f70fed277cde205314e0c15b1d64 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Thu, 9 Mar 2023 10:10:34 -0500 Subject: [PATCH 05/79] no dzdo necessary if all LSDC servers are run by service user --- lsdcServer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsdcServer b/lsdcServer index e577bd20..7e218a9b 100755 --- a/lsdcServer +++ b/lsdcServer @@ -1,3 +1,3 @@ -dzdo pkill -KILL -f daq_main2.py +pkill -KILL -f daq_main2.py source /opt/conda_envs/lsdc-server-2023-1-latest/bin/activate $LSDCHOME/daq_main2.py From bfe2103572327bacdab43494f4a0fe8bde6644c2 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Thu, 23 Mar 2023 09:53:34 -0400 Subject: [PATCH 06/79] check that an lsdc service user is running the script * ensure that a normal user isn't starting up LSDC --- daq_main2.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/daq_main2.py b/daq_main2.py index e9021ef7..9a1d70db 100755 --- a/daq_main2.py +++ b/daq_main2.py @@ -12,6 +12,7 @@ from robot_lib import * from beamline_lib import * from gov_lib import setGovRobot +import getpass import logging from logging import handlers @@ -27,6 +28,13 @@ logger.addHandler(handler1) logger.addHandler(handler2) +if not getpass.getuser().startswith("lsdc-"): + message = "LSDC server not being started by a LSDC service user account, aborting!" + print(message) + logger.error(message) + sys.exit(1) +else: + print(f"continuing as we are using a service user: {getpass.getuser()}") sitefilename = "" global command_list,immediate_command_list,z command_list = [] From 7ae433a7f84fb4607bb97434daad11417b302803 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Thu, 4 May 2023 11:39:28 -0400 Subject: [PATCH 07/79] Commenting out light brightness button until PVs are fixed --- gui/control_main.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index a856a02c..5885d6dd 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -1127,9 +1127,9 @@ def createSampleTab(self): hBoxVidControlLayout.addWidget(focusLabel) hBoxVidControlLayout.addWidget(focusPlusButton) hBoxVidControlLayout.addWidget(focusMinusButton) - hBoxVidControlLayout.addWidget(lightLevelLabel) - hBoxVidControlLayout.addWidget(sampleBrighterButton) - hBoxVidControlLayout.addWidget(sampleDimmerButton) + # hBoxVidControlLayout.addWidget(lightLevelLabel) + # hBoxVidControlLayout.addWidget(sampleBrighterButton) + # hBoxVidControlLayout.addWidget(sampleDimmerButton) hBoxVidControlLayout.addWidget(annealButton) hBoxVidControlLayout.addWidget(annealTimeLabel) hBoxVidControlLayout.addWidget(self.annealTime_ledit) @@ -3718,8 +3718,10 @@ def addRequestsToAllSelectedCB(self): elif itemDataType == "request": selectedSampleRequest = db_lib.getRequestByID(item.data(32)) self.selectedSampleID = selectedSampleRequest["sample"] - - if self.selectedSampleID in samplesConsidered: # If a request is already added to the sample, move on + + if ( + self.selectedSampleID in samplesConsidered + ): # If a request is already added to the sample, move on continue try: From 2f5224a697c7b8967944e184cc6837b60c8ec894 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Thu, 4 May 2023 12:33:33 -0400 Subject: [PATCH 08/79] Disabling brighter and dimmer buttons until PV is fixed --- gui/control_main.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 5885d6dd..0c7ddb4f 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -1092,9 +1092,11 @@ def createSampleTab(self): sampleBrighterButton = QtWidgets.QPushButton("+") sampleBrighterButton.setFixedWidth(30) sampleBrighterButton.clicked.connect(self.lightUpCB) + sampleBrighterButton.setEnabled(False) # Disabling until PV is fixed sampleDimmerButton = QtWidgets.QPushButton("-") sampleDimmerButton.setFixedWidth(30) sampleDimmerButton.clicked.connect(self.lightDimCB) + sampleDimmerButton.setEnabled(False) # Disabling until PV is fixed focusLabel = QtWidgets.QLabel("Focus") focusLabel.setAlignment(QtCore.Qt.AlignRight | Qt.AlignVCenter) focusPlusButton = QtWidgets.QPushButton("+") @@ -1127,9 +1129,9 @@ def createSampleTab(self): hBoxVidControlLayout.addWidget(focusLabel) hBoxVidControlLayout.addWidget(focusPlusButton) hBoxVidControlLayout.addWidget(focusMinusButton) - # hBoxVidControlLayout.addWidget(lightLevelLabel) - # hBoxVidControlLayout.addWidget(sampleBrighterButton) - # hBoxVidControlLayout.addWidget(sampleDimmerButton) + hBoxVidControlLayout.addWidget(lightLevelLabel) + hBoxVidControlLayout.addWidget(sampleBrighterButton) + hBoxVidControlLayout.addWidget(sampleDimmerButton) hBoxVidControlLayout.addWidget(annealButton) hBoxVidControlLayout.addWidget(annealTimeLabel) hBoxVidControlLayout.addWidget(self.annealTime_ledit) From b7ab8c274b0a433c1bc29cc6957df624e64097f4 Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Sat, 6 May 2023 11:41:06 -0400 Subject: [PATCH 09/79] Fixes: - Lifetime calculations are updated 1 second after any change using a timer - Vector length is only considered if the protocol is selected - Transmission range is specific to beamline --- config_params.py | 5 ++++- gui/control_main.py | 37 +++++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/config_params.py b/config_params.py index 26038eae..6b5d241d 100644 --- a/config_params.py +++ b/config_params.py @@ -83,4 +83,7 @@ class RasterStatus(Enum): VALID_DET_DIST = {'amx':{'min': 100, 'max':500, 'digits':3}, 'fmx':{'min':137, 'max':2000, 'digits':2}, 'nyx':{'min':100, 'max':500, 'digits':3}} VALID_TOTAL_EXP_TIMES = {'amx':{'min':0.005, 'max':300, 'digits':3}, 'fmx':{'min':0.01, 'max':300, 'digits':3}, 'nyx':{'min':0.01, 'max':1000, 'digits':3}} VALID_PREFIX_LENGTH = 25 #TODO centralize with spreadsheet validation? -VALID_PREFIX_NAME = '[0-9a-zA-Z-_]{0,%s}' % VALID_PREFIX_LENGTH \ No newline at end of file +VALID_PREFIX_NAME = '[0-9a-zA-Z-_]{0,%s}' % VALID_PREFIX_LENGTH +VALID_TRANSMISSION = {'amx': {'min': 0.001, 'max': 1.0, 'digits': 3}, + 'fmx': {'min': 0.001, 'max': 1.0, 'digits': 3}, + 'nyx': {'min': 0.001, 'max': 0.999, 'digits': 3}} \ No newline at end of file diff --git a/gui/control_main.py b/gui/control_main.py index a856a02c..c7fbb7de 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -31,6 +31,7 @@ VALID_DET_DIST, VALID_EXP_TIMES, VALID_TOTAL_EXP_TIMES, + VALID_TRANSMISSION, RasterStatus, cryostreamTempPV, ) @@ -195,6 +196,14 @@ def __init__(self): self.beamSize_pv = PV(daq_utils.beamlineComm + "size_mode") self.energy_pv = PV(daq_utils.motor_dict["energy"] + ".RBV") self.rasterStepDefs = {"Coarse": 20.0, "Fine": 10.0, "VFine": 5.0} + + # Timer that waits for a second before calling raddose 3d + # This is to prevent multiple calls when transmission textbox is changing + self.raddoseTimer = QTimer() + self.raddoseTimer.setSingleShot(True) + self.raddoseTimer.setInterval(1000) + self.raddoseTimer.timeout.connect(self.spawnRaddoseThread) + self.createSampleTab() self.initCallbacks() @@ -511,7 +520,9 @@ def createSampleTab(self): transmisionSPLabel = QtWidgets.QLabel("SetPoint:") self.transmission_ledit = self.transmissionSetPoint.getEntry() - self.transmission_ledit.setValidator(QtGui.QDoubleValidator(0.001, 0.999, 3)) + self.transmission_ledit.setValidator(QtGui.QDoubleValidator(VALID_TRANSMISSION[daq_utils.beamline]["min"], + VALID_TRANSMISSION[daq_utils.beamline]["max"], + VALID_TRANSMISSION[daq_utils.beamline]["digits"])) self.setGuiValues({"transmission": getBlConfig("stdTrans")}) self.transmission_ledit.returnPressed.connect(self.setTransCB) if daq_utils.beamline == "fmx": @@ -2536,6 +2547,7 @@ def protoComboActivatedCB(self, text): ) self.osc_start_ledit.setEnabled(True) self.osc_end_ledit.setEnabled(True) + self.calcLifetimeCB() elif protocol == "burn": self.setGuiValues( { @@ -2560,6 +2572,7 @@ def protoComboActivatedCB(self, text): self.osc_start_ledit.setEnabled(True) self.osc_end_ledit.setEnabled(True) self.protoVectorRadio.setChecked(True) + self.calcLifetimeCB() else: self.protoOtherRadio.setChecked(True) self.totalExpChanged("") @@ -2701,10 +2714,14 @@ def setLifetimeCB(self, lifetime): self.sampleLifetimeReadback_ledit.setStyleSheet("color : black") def calcLifetimeCB(self): + self.raddoseTimer.start() + if hasattr(self, "sampleLifetimeReadback_ledit"): + self.sampleLifetimeReadback_ledit.setStyleSheet("color : gray") + + def spawnRaddoseThread(self): if not os.path.exists("2vb1.pdb"): os.system("cp -a $CONFIGDIR/2vb1.pdb .") os.system("mkdir rd3d") - energyReadback = self.energy_pv.get() / 1000.0 sampleFlux = self.sampleFluxPV.get() if hasattr(self, "transmission_ledit") and hasattr( @@ -2717,15 +2734,15 @@ def calcLifetimeCB(self): except Exception as e: logger.info(f"Exception while calculating sample flux {e}") logger.info("sample flux = " + str(sampleFlux)) - try: - vecLen_s = self.vecLenLabelOutput.text() - if vecLen_s != "---": - vecLen = float(vecLen_s) - else: - vecLen = 0 - except: - vecLen = 0 + # Read vector length only if the vector protocol is chosen + vecLen = 0 + if self.protoVectorRadio.isChecked(): + try: + vecLen = float(self.vecLenLabelOutput.text()) + except: + pass wedge = float(self.osc_end_ledit.text()) + try: raddose_thread = RaddoseThread( parent=self, From baa1d1a6cf01cabfe98f4b47827a20422a3c2e7f Mon Sep 17 00:00:00 2001 From: Shekar V Date: Sat, 6 May 2023 11:47:24 -0400 Subject: [PATCH 10/79] Ran black formatter --- config_params.py | 85 +++++++++++++++++++++++++++------------------ gui/control_main.py | 16 +++++---- 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/config_params.py b/config_params.py index 6b5d241d..894adee9 100644 --- a/config_params.py +++ b/config_params.py @@ -3,36 +3,39 @@ # BlConfig parameter variable names # rastering parameters -RASTER_TUNE_LOW_RES = 'rasterTuneLowRes' -RASTER_TUNE_HIGH_RES = 'rasterTuneHighRes' -RASTER_TUNE_ICE_RING_FLAG = 'rasterTuneIceRingFlag' -RASTER_TUNE_RESO_FLAG = 'rasterTuneResoFlag' -RASTER_TUNE_ICE_RING_WIDTH = 'rasterTuneIceRingWidth' -RASTER_DOZOR_SPOT_LEVEL = 'rasterDozorSpotLevel' -RASTER_NUM_CELLS_DELAY_THRESHOLD = 'rasterNumCellsThresholdDelay' +RASTER_TUNE_LOW_RES = "rasterTuneLowRes" +RASTER_TUNE_HIGH_RES = "rasterTuneHighRes" +RASTER_TUNE_ICE_RING_FLAG = "rasterTuneIceRingFlag" +RASTER_TUNE_RESO_FLAG = "rasterTuneResoFlag" +RASTER_TUNE_ICE_RING_WIDTH = "rasterTuneIceRingWidth" +RASTER_DOZOR_SPOT_LEVEL = "rasterDozorSpotLevel" +RASTER_NUM_CELLS_DELAY_THRESHOLD = "rasterNumCellsThresholdDelay" # timing delays -ISPYB_RESULT_ENTRY_DELAY = 'ispybResultEntryDelay' -RASTER_LONG_SNAPSHOT_DELAY = 'rasterLongSnapshotDelay' -RASTER_SHORT_SNAPSHOT_DELAY = 'rasterShortSnapshotDelay' -RASTER_POST_SNAPSHOT_DELAY = 'rasterPostSnapshotDelay' -RASTER_GUI_XREC_FILL_DELAY = 'rasterGuiXrecFillDelay' +ISPYB_RESULT_ENTRY_DELAY = "ispybResultEntryDelay" +RASTER_LONG_SNAPSHOT_DELAY = "rasterLongSnapshotDelay" +RASTER_SHORT_SNAPSHOT_DELAY = "rasterShortSnapshotDelay" +RASTER_POST_SNAPSHOT_DELAY = "rasterPostSnapshotDelay" +RASTER_GUI_XREC_FILL_DELAY = "rasterGuiXrecFillDelay" # governor transition gain/exposure times -LOW_MAG_GAIN_DA = 'lowMagGainDA' -LOW_MAG_GAIN = 'lowMagGain' -LOW_MAG_EXP_TIME_DA = 'lowMagExptimeDA' -LOW_MAG_EXP_TIME = 'lowMagExptime' +LOW_MAG_GAIN_DA = "lowMagGainDA" +LOW_MAG_GAIN = "lowMagGain" +LOW_MAG_EXP_TIME_DA = "lowMagExptimeDA" +LOW_MAG_EXP_TIME = "lowMagExptime" # top view -TOP_VIEW_CHECK = 'topViewCheck' +TOP_VIEW_CHECK = "topViewCheck" -CRYOSTREAM_ONLINE = 'cryostream_online' # consistent naming with hardware, such as robot_online +CRYOSTREAM_ONLINE = ( + "cryostream_online" # consistent naming with hardware, such as robot_online +) # constant values below # GUI default configuration -BEAM_CHECK = 'beamCheck' -UNMOUNT_COLD_CHECK = 'unmountColdCheck' +BEAM_CHECK = "beamCheck" +UNMOUNT_COLD_CHECK = "unmountColdCheck" + # raster request status updates class RasterStatus(Enum): @@ -41,12 +44,14 @@ class RasterStatus(Enum): database. The GUI can process raster data, e.g. filling heat maps, in parallel to the server moving motors and adjusting low mag cam. """ + NEW = 0 DRAWN = 1 READY_FOR_FILL = 2 READY_FOR_SNAPSHOT = 3 READY_FOR_REPROCESS = 4 + HUTCH_TIMER_DELAY = 500 SAMPLE_TIMER_DELAY = 0 @@ -74,16 +79,30 @@ class RasterStatus(Enum): GOVERNOR_TIMEOUT = 120 # seconds for a governor move -DEWAR_SECTORS = {'amx':8, 'fmx':8, 'nyx':5} -PUCKS_PER_DEWAR_SECTOR = {'amx':3, 'fmx':3, 'nyx':3} - -cryostreamTempPV = {'amx': 'AMX:cs700:gasT-I', 'fmx': 'FMX:cs700:gasT-I'} - -VALID_EXP_TIMES = {'amx':{'min':0.005, 'max':1, 'digits':3}, 'fmx':{'min':0.01, 'max':10, 'digits':3}, 'nyx':{'min':0.002, 'max':10, 'digits':4}} -VALID_DET_DIST = {'amx':{'min': 100, 'max':500, 'digits':3}, 'fmx':{'min':137, 'max':2000, 'digits':2}, 'nyx':{'min':100, 'max':500, 'digits':3}} -VALID_TOTAL_EXP_TIMES = {'amx':{'min':0.005, 'max':300, 'digits':3}, 'fmx':{'min':0.01, 'max':300, 'digits':3}, 'nyx':{'min':0.01, 'max':1000, 'digits':3}} -VALID_PREFIX_LENGTH = 25 #TODO centralize with spreadsheet validation? -VALID_PREFIX_NAME = '[0-9a-zA-Z-_]{0,%s}' % VALID_PREFIX_LENGTH -VALID_TRANSMISSION = {'amx': {'min': 0.001, 'max': 1.0, 'digits': 3}, - 'fmx': {'min': 0.001, 'max': 1.0, 'digits': 3}, - 'nyx': {'min': 0.001, 'max': 0.999, 'digits': 3}} \ No newline at end of file +DEWAR_SECTORS = {"amx": 8, "fmx": 8, "nyx": 5} +PUCKS_PER_DEWAR_SECTOR = {"amx": 3, "fmx": 3, "nyx": 3} + +cryostreamTempPV = {"amx": "AMX:cs700:gasT-I", "fmx": "FMX:cs700:gasT-I"} + +VALID_EXP_TIMES = { + "amx": {"min": 0.005, "max": 1, "digits": 3}, + "fmx": {"min": 0.01, "max": 10, "digits": 3}, + "nyx": {"min": 0.002, "max": 10, "digits": 4}, +} +VALID_DET_DIST = { + "amx": {"min": 100, "max": 500, "digits": 3}, + "fmx": {"min": 137, "max": 2000, "digits": 2}, + "nyx": {"min": 100, "max": 500, "digits": 3}, +} +VALID_TOTAL_EXP_TIMES = { + "amx": {"min": 0.005, "max": 300, "digits": 3}, + "fmx": {"min": 0.01, "max": 300, "digits": 3}, + "nyx": {"min": 0.01, "max": 1000, "digits": 3}, +} +VALID_PREFIX_LENGTH = 25 # TODO centralize with spreadsheet validation? +VALID_PREFIX_NAME = "[0-9a-zA-Z-_]{0,%s}" % VALID_PREFIX_LENGTH +VALID_TRANSMISSION = { + "amx": {"min": 0.001, "max": 1.0, "digits": 3}, + "fmx": {"min": 0.001, "max": 1.0, "digits": 3}, + "nyx": {"min": 0.001, "max": 0.999, "digits": 3}, +} diff --git a/gui/control_main.py b/gui/control_main.py index c7fbb7de..65d26398 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -520,9 +520,13 @@ def createSampleTab(self): transmisionSPLabel = QtWidgets.QLabel("SetPoint:") self.transmission_ledit = self.transmissionSetPoint.getEntry() - self.transmission_ledit.setValidator(QtGui.QDoubleValidator(VALID_TRANSMISSION[daq_utils.beamline]["min"], - VALID_TRANSMISSION[daq_utils.beamline]["max"], - VALID_TRANSMISSION[daq_utils.beamline]["digits"])) + self.transmission_ledit.setValidator( + QtGui.QDoubleValidator( + VALID_TRANSMISSION[daq_utils.beamline]["min"], + VALID_TRANSMISSION[daq_utils.beamline]["max"], + VALID_TRANSMISSION[daq_utils.beamline]["digits"], + ) + ) self.setGuiValues({"transmission": getBlConfig("stdTrans")}) self.transmission_ledit.returnPressed.connect(self.setTransCB) if daq_utils.beamline == "fmx": @@ -2742,7 +2746,7 @@ def spawnRaddoseThread(self): except: pass wedge = float(self.osc_end_ledit.text()) - + try: raddose_thread = RaddoseThread( parent=self, @@ -3735,8 +3739,8 @@ def addRequestsToAllSelectedCB(self): elif itemDataType == "request": selectedSampleRequest = db_lib.getRequestByID(item.data(32)) self.selectedSampleID = selectedSampleRequest["sample"] - - if self.selectedSampleID in samplesConsidered: # If a request is already added to the sample, move on + # If a request is already added to the sample, move on + if self.selectedSampleID in samplesConsidered: continue try: From 8f4ab1c61fccd1d90e544f7d98792ef9cf5658ad Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Mon, 8 May 2023 09:26:13 -0400 Subject: [PATCH 11/79] Increased sleep time after loading hdf5 file from 0.3 to 0.5 Check if beamline is fmx before calling calcLifetime while changing protocols --- albulaUtils.py | 2 +- gui/control_main.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/albulaUtils.py b/albulaUtils.py index fb146e89..134548e5 100644 --- a/albulaUtils.py +++ b/albulaUtils.py @@ -91,7 +91,7 @@ def _albulaDispFile(filename): logger.info('reading file: %s' % filename[0]) albulaSubFrame.loadFile(filename[0]) currentMasterH5 = filename[0] - sleep(0.3) # Sleep to allow Albula to load file. Otherwise the following goTo() is ignored + sleep(0.5) # Sleep to allow Albula to load file. Otherwise the following goTo() is ignored logger.debug('reading image number %s' % filename[1]) albulaSubFrame.goTo(filename[1]) except dectris.albula.DNoObject: diff --git a/gui/control_main.py b/gui/control_main.py index c7fbb7de..8ecbd79c 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -2547,7 +2547,8 @@ def protoComboActivatedCB(self, text): ) self.osc_start_ledit.setEnabled(True) self.osc_end_ledit.setEnabled(True) - self.calcLifetimeCB() + if daq_utils.beamline == 'fmx': + self.calcLifetimeCB() elif protocol == "burn": self.setGuiValues( { @@ -2572,7 +2573,8 @@ def protoComboActivatedCB(self, text): self.osc_start_ledit.setEnabled(True) self.osc_end_ledit.setEnabled(True) self.protoVectorRadio.setChecked(True) - self.calcLifetimeCB() + if daq_utils.beamline == 'fmx': + self.calcLifetimeCB() else: self.protoOtherRadio.setChecked(True) self.totalExpChanged("") From 7a3c09728df0e70791374f409280d708f8fcc6e5 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Mon, 8 May 2023 11:16:29 -0400 Subject: [PATCH 12/79] Update control_main.py Removed formatting --- gui/control_main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 0c7ddb4f..41320d5a 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -3721,9 +3721,7 @@ def addRequestsToAllSelectedCB(self): selectedSampleRequest = db_lib.getRequestByID(item.data(32)) self.selectedSampleID = selectedSampleRequest["sample"] - if ( - self.selectedSampleID in samplesConsidered - ): # If a request is already added to the sample, move on + if self.selectedSampleID in samplesConsidered: # If a request is already added to the sample, move on continue try: From da7090d202583d489f340eb6dd0369dac9a7af42 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Thu, 11 May 2023 16:15:16 -0400 Subject: [PATCH 13/79] calcLifetime triggers when osc range is edited --- gui/control_main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 25f1ef8a..78bebe80 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -408,6 +408,7 @@ def createSampleTab(self): self.osc_end_ledit.textChanged[str].connect( functools.partial(self.totalExpChanged, "oscEnd") ) + self.osc_end_ledit.textChanged.connect(self.calcLifetimeCB) hBoxColParams1.addWidget(colStartLabel) hBoxColParams1.addWidget(self.osc_start_ledit) hBoxColParams1.addWidget(self.colEndLabel) @@ -2553,7 +2554,7 @@ def protoComboActivatedCB(self, text): ) self.osc_start_ledit.setEnabled(True) self.osc_end_ledit.setEnabled(True) - if daq_utils.beamline == 'fmx': + if daq_utils.beamline == "fmx": self.calcLifetimeCB() elif protocol == "burn": self.setGuiValues( @@ -2579,7 +2580,7 @@ def protoComboActivatedCB(self, text): self.osc_start_ledit.setEnabled(True) self.osc_end_ledit.setEnabled(True) self.protoVectorRadio.setChecked(True) - if daq_utils.beamline == 'fmx': + if daq_utils.beamline == "fmx": self.calcLifetimeCB() else: self.protoOtherRadio.setChecked(True) From 73f091ee9282d73a8e1dbb14a0c37dba12cbbe73 Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Fri, 19 May 2023 16:01:18 -0400 Subject: [PATCH 14/79] Fixed a case where multiple users write to the same raddose output --- gui/control_main.py | 5 ++-- raddoseLib.py | 60 ++++++++++++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 86f5420e..6689564b 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -408,6 +408,7 @@ def createSampleTab(self): self.osc_end_ledit.textChanged[str].connect( functools.partial(self.totalExpChanged, "oscEnd") ) + self.osc_end_ledit.textChanged.connect(self.calcLifetimeCB) hBoxColParams1.addWidget(colStartLabel) hBoxColParams1.addWidget(self.osc_start_ledit) hBoxColParams1.addWidget(self.colEndLabel) @@ -2747,9 +2748,9 @@ def spawnRaddoseThread(self): vecLen = float(self.vecLenLabelOutput.text()) except: pass - wedge = float(self.osc_end_ledit.text()) - + try: + wedge = float(self.osc_end_ledit.text()) raddose_thread = RaddoseThread( parent=self, beamsizeV=3.0, diff --git a/raddoseLib.py b/raddoseLib.py index 841fe033..84449c45 100755 --- a/raddoseLib.py +++ b/raddoseLib.py @@ -8,6 +8,7 @@ import os.path import os import logging +import traceback logger = logging.getLogger(__name__) #12/19 - See Martin about this code! @@ -19,8 +20,22 @@ def replaceLine(file,searchExp,replaceExp): line = replaceExp sys.stdout.write(line) +def replaceLines(filename, replacementStrings): + lines = [] + with open(filename, 'r') as f: + for line in f.readlines(): + for text_to_search, replacement_text in replacementStrings.items(): + if text_to_search in line: + line = replacement_text + break + lines.append(line) + + with open(filename, 'w') as f: + f.writelines(lines) + + def run_rd3d(inputFileName): - prc = subprocess.Popen(["java", "-jar", os.environ['CONFIGDIR'] + "/raddose3d.jar", "-i", inputFileName, "-p", "rd3d/rd3d_"], + prc = subprocess.Popen(["java", "-jar", os.environ['CONFIGDIR'] + "/raddose3d.jar", "-i", inputFileName, "-p", f"rd3d/rd3d_{os.getpid()}_"], stdout=subprocess.PIPE, universal_newlines=True) out = prc.communicate()[0] @@ -98,31 +113,35 @@ def rd3d_calc(flux=3.5e12, energy=12.66, """ rd3d_dir = "rd3d" - inputFileName = "rd3d_input.txt" - outputFileName = "rd3d_Summary.csv" + inputFileName = f"rd3d_{os.getpid()}_input.txt" + outputFileName = f"rd3d_{os.getpid()}_Summary.csv" templateFilePath=os.path.join(rd3d_dir,templateFileName) inputFilePath=os.path.join(rd3d_dir,inputFileName) outputFilePath=os.path.join(rd3d_dir,outputFileName) copyfile(templateFilePath, inputFilePath) - - replaceLine(inputFilePath,"FLUX",'FLUX {:.2e}\n'.format(flux)) - replaceLine(inputFilePath,"ENERGY",'ENERGY {:.2f}\n'.format(energy)) - replaceLine(inputFilePath,"TYPE GAUSSIAN",'TYPE {:s}\n'.format(beamType)) - replaceLine(inputFilePath,"FWHM",'FWHM {:.1f} {:.1f}\n'.format(fwhmX,fwhmY)) - replaceLine(inputFilePath,"COLLIMATION",'COLLIMATION RECTANGULAR {:.1f} {:.1f}\n'.format(collimationX,collimationY)) - replaceLine(inputFilePath,"WEDGE",'WEDGE 0 {:0.1f}\n'.format(wedge)) - replaceLine(inputFilePath,"EXPOSURETIME",'EXPOSURETIME {:0.3f}\n'.format(exposureTime)) - replaceLine(inputFilePath,"TRANSLATEPERDEGREE", - 'TRANSLATEPERDEGREE {:0.4f} {:0.4f} {:0.4f}\n'.format(translatePerDegX,translatePerDegY,translatePerDegZ)) - replaceLine(inputFilePath,"DIMENSION",'DIMENSION {:0.1f} {:0.1f} {:0.1f}\n'.format(dimX,dimY,dimZ)) - replaceLine(inputFilePath,"PIXELSPERMICRON",'PIXELSPERMICRON {:0.1f}\n'.format(pixelsPerMicron)) - replaceLine(inputFilePath,"ANGULARRESOLUTION",'ANGULARRESOLUTION {:0.1f}\n'.format(angularResolution)) - replaceLine(inputFilePath,"STARTOFFSET", - 'STARTOFFSET {:f} {:f} {:f}\n'.format(startOffsetX,startOffsetY,startOffsetZ)) - - out = run_rd3d(inputFilePath) + + replacementStrings = { + "FLUX":'FLUX {:.2e}\n'.format(flux), + "ENERGY":'ENERGY {:.2f}\n'.format(energy), + "TYPE GAUSSIAN":'TYPE {:s}\n'.format(beamType), + "FWHM":'FWHM {:.1f} {:.1f}\n'.format(fwhmX,fwhmY), + "COLLIMATION":'COLLIMATION RECTANGULAR {:.1f} {:.1f}\n'.format(collimationX,collimationY), + "WEDGE":'WEDGE 0 {:0.1f}\n'.format(wedge), + "EXPOSURETIME":'EXPOSURETIME {:0.3f}\n'.format(exposureTime), + "TRANSLATEPERDEGREE": + 'TRANSLATEPERDEGREE {:0.4f} {:0.4f} {:0.4f}\n'.format(translatePerDegX,translatePerDegY,translatePerDegZ), + "DIMENSION":'DIMENSION {:0.1f} {:0.1f} {:0.1f}\n'.format(dimX,dimY,dimZ), + "PIXELSPERMICRON":'PIXELSPERMICRON {:0.1f}\n'.format(pixelsPerMicron), + "ANGULARRESOLUTION":'ANGULARRESOLUTION {:0.1f}\n'.format(angularResolution), + "STARTOFFSET": + 'STARTOFFSET {:f} {:f} {:f}\n'.format(startOffsetX,startOffsetY,startOffsetZ) + } + + replaceLines(inputFilePath, replacementStrings) + + out = run_rd3d(inputFilePath, outputFilePath) if verbose: logger.info(out) @@ -276,6 +295,7 @@ def fmx_expTime(avg_dwd = 10, #Default of 10MGy dose1s = rd3d_out['DWD'].item() # .item() to convert 1d array to scalar logger.info('Average Diffraction Weighted Dose for 1s exposure = {:f} MGy'.format(dose1s)) except Exception as e: + traceback.print_exc() logger.error(f'Exception in rd3d calc: {e}') dose1s = 0 From 7cebe9337299d07835150afa31a48e391ff73e74 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Thu, 25 May 2023 17:25:00 -0400 Subject: [PATCH 15/79] Fixed wrong parameters passed to mount function when retrying after error --- embl_robot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embl_robot.py b/embl_robot.py index ee719cc4..5982241c 100644 --- a/embl_robot.py +++ b/embl_robot.py @@ -310,7 +310,7 @@ def mount(self, gov_robot, puckPos,pinPos,sampID,**kwargs): else: if (retryMountCount == 0): retryMountCount+=1 - mountStat = self.mount(puckPos,pinPos,sampID, kwargs) + mountStat = self.mount(gov_robot, puckPos,pinPos,sampID, **kwargs) if (mountStat == MOUNT_STEP_SUCCESSFUL): retryMountCount = 0 return mountStat From 67544e55614cf363cedb9b53ddedcc01e90c3e78 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Thu, 15 Jun 2023 13:30:00 -0400 Subject: [PATCH 16/79] Added code to save FMX flux reference after setE - Also moves to correct governor states after setE --- setenergy_lsdc.py | 258 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 243 insertions(+), 15 deletions(-) diff --git a/setenergy_lsdc.py b/setenergy_lsdc.py index 685ad72f..f536ac3a 100644 --- a/setenergy_lsdc.py +++ b/setenergy_lsdc.py @@ -17,7 +17,11 @@ import time import gov_lib from start_bs import db, gov_robot, govs +import logging + +logger = logging.getLogger() +flux_df = None # Machine ========================================================== beam_current = EpicsSignal('SR:OPS-BI{DCCT:1}I:Real-I') @@ -205,6 +209,17 @@ class Bpm(Device): bpm1_sum_all_precision = EpicsSignal('XF:17IDA-BI:FMX{BPM:1}SumAll:MeanValue_RBV.PREC') bpm1_sum_all_precision.put(10) +class Xbpm(Device): + x = Cpt(EpicsSignalRO, 'Pos:X-I') + y = Cpt(EpicsSignalRO, 'Pos:Y-I') + a = Cpt(EpicsSignalRO, 'Ampl:ACurrAvg-I') + b = Cpt(EpicsSignalRO, 'Ampl:BCurrAvg-I') + c = Cpt(EpicsSignalRO, 'Ampl:CCurrAvg-I') + d = Cpt(EpicsSignalRO, 'Ampl:DCurrAvg-I') + total = Cpt(EpicsSignalRO, 'Ampl:CurrTotal-I') + +xbpm2 = Xbpm('SR:C17-BI{XBPM:2}', name='xbpm2') + # Attenuators, CRL ========================================================================= class Transmission(Device): @@ -239,6 +254,10 @@ class AttenuatorBCU(Device): read_attrs=['done', 'a1', 'a2', 'a3', 'a4'], labels=['fmx']) +# KB mirror pitch tweak voltages +vkb_piezo_tweak = EpicsSignal('XF:17IDC-BI:FMX{Best:2}:PreDAC0:OutCh1') +hkb_piezo_tweak = EpicsSignal('XF:17IDC-BI:FMX{Best:2}:PreDAC0:OutCh2') + # Utility ================================================================================= class BeamlineCalibrations(Device): @@ -698,6 +717,8 @@ def setELsdc(energy, if slit1Set: yield from bps.mv(slits1.x_gap, slits1XGapOrg) # Move Slit 1 X to original position yield from bps.mv(slits1.y_gap, slits1YGapOrg) # Move Slit 1 Y to original position + + yield from fmx_reference(transSet=transSet) # Alignment =========================================================================================== @@ -939,21 +960,6 @@ def beam_center_align(transSet='All'): beamHiMagDiffX=0 beamHiMagDiffY=0 - # # Do nothing if we would walk ROI2 for Mag 3 off the top camera edge - # if (cam_8.roi2.min_xyz.min_y.get() + beamHiMagDiffY + cam_8.roi2.size.y.get()) > 1215 - # print('ROI2 for Mag 3 too high.', - # 'Move beam towards center of Hi Mag, or align beam center by hand', - # 'No changes made. Manual beam center correction needed.') - # beamHiMagDiffX=0 - # beamHiMagDiffY=0 - # - # # Do nothing if we would walk ROI2 for Mag 3 off the bottom camera edge - # if (cam_8.roi2.min_xyz.min_y.get() + beamHiMagDiffY < 1 - # print('ROI2 for Mag 3 too low.', - # 'Move beam towards center of Hi Mag, or align beam center by hand', - # 'No changes made. Manual beam center correction needed.') - # beamHiMagDiffX=0 - # beamHiMagDiffY=0 # Correct Mag 4 (cam_8 ROI1) # Adjust cam_8 ROI1 min_y, LSDC uses this for the Mag4 FOV. @@ -1002,3 +1008,225 @@ def beam_center_align(transSet='All'): yield from trans_set(transOrgBCU, trans=trans_bcu) else: yield from trans_set(transOrgBCU, trans=trans_bcu) + +def get_fluxKeithley(): + """ + Returns Keithley diode current derived flux. + """ + + keithFlux = epics.caget('XF:17IDA-OP:FMX{Mono:DCM-dflux}') + + return keithFlux + + +def set_fluxBeam(flux): + """ + Sets the flux reference field. + + flux: Beamline flux at sample position for transmisison T = 1. [ph/s] + """ + + error = epics.caput('XF:17IDA-OP:FMX{Mono:DCM-dflux-M}', flux) + + return error + + +def slit1_flux_reference(flux_df,slit1Gap): + """ + Sets Slit 1 X gap and Slit 1 Y gap to a specified position, + and returns flux reference values to a provided pandas DataFrame. + + Supporting function for fmx_flux_reference() + + Parameters + ---------- + + slit1Gap: float + Gap value for Slit 1 X and Y [um] + + flux_df: pandas DataFrame with fields: + Slit 1 X gap [um] + Slit 1 Y gap [um] + Keithley current [A] + Keithley flux [ph/s] + BPM1 sum [A] + BPM4 sum [A] + + """ + yield from bps.mv(slits1.x_gap, slit1Gap, slits1.y_gap, slit1Gap, wait=True) + time.sleep(2.0) # wait so the slits are definitely done moving and the Keithley reading is stable + + flux_df.at[slit1Gap, 'Slit 1 X gap [um]'] = slit1Gap + flux_df.at[slit1Gap, 'Slit 1 Y gap [um]'] = slit1Gap + flux_df.at[slit1Gap, 'Keithley current [A]'] = keithley.get() + flux_df.at[slit1Gap, 'Keithley flux [ph/s]'] = get_fluxKeithley() + flux_df.at[slit1Gap, 'BPM1 sum [A]'] = bpm1.sum_all.get() + # TEMP FIX: flux_df.at[slit1Gap, 'BPM4 sum [A]'] = bpm4.sum_all.get() + flux_df.at[slit1Gap, 'BPM4 sum [A]'] = 0 + +def fmx_flux_reference(slit1GapList = [2000, 1000, 600, 400], slit1GapDefault = 1000, transSet='All'): + """ + Sets Slit 1 X gap and Slit 1 Y gap to a list of settings, + and returns flux reference values in a pandas DataFrame. + + Parameters + ---------- + + slit1GapList: float (default=[2000, 1000, 600, 400]) + A list of gap values [um] for Slit 1 X and Y + slit1GapDefault: Gap value [um] to set as default after getting references + Default slit1GapDefault = 1000 + + Returns + ------- + + flux_df: pandas DataFrame with fields + Slit 1 X gap [um] + Slit 1 Y gap [um] + Keithley current [A] + Keithley flux [ph/s] + BPM1 sum [A] + BPM4 sum [A] + + Examples + -------- + fmx_flux_reference() + flux_df=fmx_flux_reference() + flux_df + fmx_flux_reference(slit1GapList = [2000, 1500, 1000]) + fmx_flux_reference(slit1GapList = [2000, 1500, 1000], slit1GapDefault = 600) + + """ + + # Store current transmission, then set full transmission + if transSet != 'None': + if transSet in ['All', 'BCU']: + transOrgBCU = trans_get(trans=trans_bcu) + if transSet in ['All', 'RI']: + transOrgRI = trans_get(trans=trans_ri) + yield from trans_set(1.0, trans=trans_ri) + if transSet == 'BCU': + yield from trans_set(1.0, trans=trans_bcu) + if transSet == 'All': + yield from trans_set(1.0, trans=trans_bcu) + + msgStr = "Energy = " + "%.1f" % get_energy() + " eV" + print(msgStr) + logger.info(msgStr) + + global flux_df + flux_df = pd.DataFrame(columns=['Slit 1 X gap [um]', + 'Slit 1 Y gap [um]', + 'Keithley current [A]', + 'Keithley flux [ph/s]', + 'BPM1 sum [A]', + 'BPM4 sum [A]', + ]) + + # Put in diode + yield from bps.mv(light.y,govPositionGet('li', 'Diode')) + + # Open BCU shutter + yield from bps.mv(shutter_bcu.open, 1) + time.sleep(1) + + for slit1Gap in slit1GapList: + yield from slit1_flux_reference(flux_df,slit1Gap) + + # Move back to default slit width + # TODO: save reference before and return to + yield from bps.mv(slits1.x_gap, slit1GapDefault, slits1.y_gap, slit1GapDefault, wait=True) + time.sleep(2.0) # wait so the slits are definitely done moving and the Keithley reading is stable + + vFlux = get_fluxKeithley() + set_fluxBeam(vFlux) + msgStr = "Reference flux for Slit 1 gap = " + "%d" % slit1GapDefault + " um for T=1 set to " + "%.1e" % vFlux + " ph/s" + print(msgStr) + logger.info(msgStr) + + + # Close shutter + yield from bps.mv(shutter_bcu.close, 1) + + # Retract diode + yield from bps.mv(light.y,govPositionGet('li', 'In')) + + # Set previous beam transmission + if transSet != 'None': + if transSet in ['All', 'RI']: + yield from trans_set(transOrgRI, trans=trans_ri) + if transSet in ['All', 'BCU']: + yield from trans_set(transOrgBCU, trans=trans_bcu) + + +def fmx_reference(slit1GapDefault = 1000, transSet='All'): + """ + Calls fmx_flux_reference, then fmx_beamline_reference. + setE will do the same after the beam alignment. + + Parameters + ---------- + transSet: FMX only: Set to 'RI' if there is a problem with the BCU attenuator. + FMX only: Set to 'BCU' if there is a problem with the RI attenuator. + Set to 'None' if there are problems with all attenuators. + Operator then has to choose a flux by hand that will not saturate scinti + default = 'All' + + Examples + -------- + fmx_reference() + + """ + if not gov_robot.state.get() == "SA": + print('Not in Governor state SA, exiting') + return + + # Transition to Governor state BL + gov_lib.setGovRobot(gov_robot, 'BL') + + yield from fmx_flux_reference(slit1GapDefault = slit1GapDefault, transSet = transSet) + + # Transition to Governor state SA + gov_lib.setGovRobot(gov_robot, 'SA') + + log_fmx_beamline_reference() + + + +def log_fmx_beamline_reference(): + """ + Prints reference values and appends to the FMX log file + + + Examples + -------- + fmx_beamline_reference() + + """ + global flux_df + messages = [ + f"Energy = {get_energy():.2f} eV", + f"Beam current = {beam_current.get():.2f} mA", + f"IVU gap = {ivu_gap.gap.user_readback.get():.1f} um", + f"XBPM2 posX = {xbpm2.x.get():.2f} um", + f"XBPM2 posY = {xbpm2.y.get():.2f} um", + f"XBPM2 total current = {xbpm2.total.get():.2f} uA", + f"HDCM pitch = {hdcm.p.user_readback.get():.4f} mrad", + f"HDCM roll = {hdcm.r.user_readback.get():.4f} mrad", + f"BPM1 posX = {bpm1.x.get():.2f}", + f"BPM1 posY = {bpm1.y.get():.2f}", + f"BPM1 total current = {bpm1.sum_all.get():.3f} A", + f"HFM pitch = {hfm.pitch.user_readback.get():.4f} mrad", + f"VKB tweak voltage = {vkb_piezo_tweak.get():.3f} V", + f"VKB pitch = {kbm.vp.user_readback.get():.4f} urad", + f"HKB tweak voltage = {hkb_piezo_tweak.get():.3f} V", + f"HKB pitch = {kbm.hp.user_readback.get():.4f} urad", + f"Gonio X = {gonio.gx.user_readback.get():.1f} um", + f"Gonio Y = {gonio.gy.user_readback.get():.1f} um", + f"Gonio Z = {gonio.gz.user_readback.get():.1f} um", + ] + for message in messages: + print(message) + logger.info(message) + if flux_df is not None: + logger.info(f"Flux dataframe {flux_df.to_string()}") From deec17872792d9675a78e85cfc2b81a385300f4e Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Mon, 26 Jun 2023 17:25:29 -0400 Subject: [PATCH 17/79] all server and GUI instances will be run from lsdc__prod * development clones of LSDC will be located at /nsls2/software/mx/daq/lsdc_ (the original location), while production clones have been checked out to /nsls2/software/mx/daq/lsdc__prod --- bin/lsdcGui_amx | 2 +- bin/lsdcGui_fmx | 2 +- bin/lsdcGui_nyx | 2 +- bin/lsdcRemote_amx.cmd | 2 +- bin/lsdcRemote_fmx.cmd | 2 +- bin/lsdcServer_amx.cmd | 2 +- bin/lsdcServer_fmx.cmd | 2 +- bin/lsdcServer_nyx.cmd | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/lsdcGui_amx b/bin/lsdcGui_amx index b39909a2..c684d14a 100755 --- a/bin/lsdcGui_amx +++ b/bin/lsdcGui_amx @@ -1,6 +1,6 @@ export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_amx +export LSDCHOME=${PROJDIR}lsdc_amx_prod export PATH=/usr/local/bin:/usr/bin:/bin export PYTHONPATH=".:${CONFIGDIR}:/opt/dectris/albula/4.0/python:${LSDCHOME}" diff --git a/bin/lsdcGui_fmx b/bin/lsdcGui_fmx index e028aee7..159fdb4c 100755 --- a/bin/lsdcGui_fmx +++ b/bin/lsdcGui_fmx @@ -1,6 +1,6 @@ export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_fmx +export LSDCHOME=${PROJDIR}lsdc_fmx_prod export PATH=/usr/local/bin:/usr/bin:/bin export PYTHONPATH=".:${CONFIGDIR}:/opt/dectris/albula/4.0/python:${LSDCHOME}" diff --git a/bin/lsdcGui_nyx b/bin/lsdcGui_nyx index 55d2122d..a0aa62ac 100755 --- a/bin/lsdcGui_nyx +++ b/bin/lsdcGui_nyx @@ -1,6 +1,6 @@ export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_nyx +export LSDCHOME=${PROJDIR}lsdc_nyx_prod export EPICS_CA_AUTO_ADDR_LIST=NO export EPICS_CA_ADDR_LIST=10.67.147.255 diff --git a/bin/lsdcRemote_amx.cmd b/bin/lsdcRemote_amx.cmd index 13f770b7..ee8f0626 100755 --- a/bin/lsdcRemote_amx.cmd +++ b/bin/lsdcRemote_amx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_amx +export LSDCHOME=${PROJDIR}lsdc_amx_prod export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env_amx.txt diff --git a/bin/lsdcRemote_fmx.cmd b/bin/lsdcRemote_fmx.cmd index 052d8241..a892611b 100755 --- a/bin/lsdcRemote_fmx.cmd +++ b/bin/lsdcRemote_fmx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_fmx +export LSDCHOME=${PROJDIR}lsdc_fmx_prod export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env.txt diff --git a/bin/lsdcServer_amx.cmd b/bin/lsdcServer_amx.cmd index 64e6d4ed..24fc70e6 100755 --- a/bin/lsdcServer_amx.cmd +++ b/bin/lsdcServer_amx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_amx +export LSDCHOME=${PROJDIR}lsdc_amx_prod export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env_amx.txt diff --git a/bin/lsdcServer_fmx.cmd b/bin/lsdcServer_fmx.cmd index 59e63cfb..096bdb41 100755 --- a/bin/lsdcServer_fmx.cmd +++ b/bin/lsdcServer_fmx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_fmx +export LSDCHOME=${PROJDIR}lsdc_fmx_prod export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env.txt diff --git a/bin/lsdcServer_nyx.cmd b/bin/lsdcServer_nyx.cmd index b06b291b..e46707bf 100755 --- a/bin/lsdcServer_nyx.cmd +++ b/bin/lsdcServer_nyx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_nyx +export LSDCHOME=${PROJDIR}lsdc_nyx_prod export EPICS_CA_AUTO_ADDR_LIST=NO export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin From be9ddd08337e4be053ee7ff6b8b012b0e4dfe77d Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 27 Jun 2023 10:48:25 -0400 Subject: [PATCH 18/79] add current LSDC service users, check current user against them * more explicitly check whether current user is a known LSDC service user --- config_params.py | 2 ++ daq_main2.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/config_params.py b/config_params.py index 07101576..abe12b7a 100644 --- a/config_params.py +++ b/config_params.py @@ -76,3 +76,5 @@ class RasterStatus(Enum): DEWAR_SECTORS = {'amx':8, 'fmx':8, 'nyx':5} PUCKS_PER_DEWAR_SECTOR = {'amx':3, 'fmx':3, 'nyx':3} + +LSDC_SERVICE_USERS = ("lsdc-amx", "lsdc-fmx", "lsdc-nyx") diff --git a/daq_main2.py b/daq_main2.py index 9a1d70db..af65f630 100755 --- a/daq_main2.py +++ b/daq_main2.py @@ -5,6 +5,7 @@ import sys import os from daq_main_common import pybass_init, run_server +from config_params import LSDC_SERVICE_USERS #TODO understand why imports are required here - GUI requires imports in daq_main_common from daq_macros import * @@ -28,7 +29,7 @@ logger.addHandler(handler1) logger.addHandler(handler2) -if not getpass.getuser().startswith("lsdc-"): +if not getpass.getuser() in LSDC_SERVICE_USERS: message = "LSDC server not being started by a LSDC service user account, aborting!" print(message) logger.error(message) From 4c8c30d998a9e4edec0bc11a05ad55c0e8e3f460 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Thu, 29 Jun 2023 11:16:29 -0400 Subject: [PATCH 19/79] use subprocess.Popen which has the cwd kwarg * os.popen does not --- top_view.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/top_view.py b/top_view.py index 4c3c732a..cd3bf9ed 100644 --- a/top_view.py +++ b/top_view.py @@ -6,6 +6,7 @@ import os import filecmp import logging +import subprocess from config_params import TOP_VIEW_CHECK logger = logging.getLogger(__name__) @@ -37,7 +38,7 @@ def wait90TopviewThread(gov_robot, prefix1,prefix90): if (not filecmp.cmp(getBlConfig("visitDirectory")+"/pinAlign/"+snapshot1Name,getBlConfig("visitDirectory")+"/pinAlign/"+snapshot2Name)): #this would mean something is wrong if true because the pictures are identical comm_s = os.environ["LSDCHOME"] + "/runPinAlign.py " + snapshot1Name + " " + snapshot2Name logger.info(comm_s) - lines = os.popen(comm_s, cwd=getBlConfig("visitDirectory")).readlines() + lines = subprocess.Popen(comm_s, cwd=getBlConfig("visitDirectory")).readlines() logger.info("printing lines right after popen ") logger.info(lines) logger.info(" done") From 29e5c7e31ebd1cb4a8ad39b711fb09aa2fa4a7c8 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Thu, 29 Jun 2023 11:42:34 -0400 Subject: [PATCH 20/79] Reading lines of subprocess.Popen() --- top_view.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/top_view.py b/top_view.py index cd3bf9ed..c0312379 100644 --- a/top_view.py +++ b/top_view.py @@ -38,7 +38,8 @@ def wait90TopviewThread(gov_robot, prefix1,prefix90): if (not filecmp.cmp(getBlConfig("visitDirectory")+"/pinAlign/"+snapshot1Name,getBlConfig("visitDirectory")+"/pinAlign/"+snapshot2Name)): #this would mean something is wrong if true because the pictures are identical comm_s = os.environ["LSDCHOME"] + "/runPinAlign.py " + snapshot1Name + " " + snapshot2Name logger.info(comm_s) - lines = subprocess.Popen(comm_s, cwd=getBlConfig("visitDirectory")).readlines() + process = subprocess.Popen(comm_s, cwd=getBlConfig("visitDirectory"), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + lines = process.stdout.readlines() logger.info("printing lines right after popen ") logger.info(lines) logger.info(" done") From 4f424d88a96ca180c8ff6a37a97c6c89aaadfaf7 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Thu, 29 Jun 2023 13:20:48 -0400 Subject: [PATCH 21/79] Using subprocess.run() instead of subprocess.Popen() in topview.py --- top_view.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/top_view.py b/top_view.py index c0312379..5e235d81 100644 --- a/top_view.py +++ b/top_view.py @@ -36,10 +36,10 @@ def wait90TopviewThread(gov_robot, prefix1,prefix90): snapshot1Name = prefix1+"_001.jpg" snapshot2Name = prefix90+"_001.jpg" if (not filecmp.cmp(getBlConfig("visitDirectory")+"/pinAlign/"+snapshot1Name,getBlConfig("visitDirectory")+"/pinAlign/"+snapshot2Name)): #this would mean something is wrong if true because the pictures are identical - comm_s = os.environ["LSDCHOME"] + "/runPinAlign.py " + snapshot1Name + " " + snapshot2Name + comm_s = [os.environ["LSDCHOME"] + "/runPinAlign.py", snapshot1Name, snapshot2Name] logger.info(comm_s) - process = subprocess.Popen(comm_s, cwd=getBlConfig("visitDirectory"), stdout=subprocess.PIPE, stderr=subprocess.PIPE) - lines = process.stdout.readlines() + process = subprocess.run(comm_s, cwd=getBlConfig("visitDirectory"), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + lines = process.stdout.decode().strip().split('\n') logger.info("printing lines right after popen ") logger.info(lines) logger.info(" done") From 74081dec4e73a7c0e5ca06cd71f7e167a6fbf700 Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Thu, 29 Jun 2023 17:02:18 -0400 Subject: [PATCH 22/79] Added fixes to make bluesky plan work --- setenergy_lsdc.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/setenergy_lsdc.py b/setenergy_lsdc.py index f536ac3a..70d21127 100644 --- a/setenergy_lsdc.py +++ b/setenergy_lsdc.py @@ -152,6 +152,10 @@ class GoniometerStack(Device): ## Light light = YMotor('XF:17IDC-ES:FMX{Light:1', name='lightY') +## Temporary: Disable motor alarm until end switch is fixed +from ophyd.utils.epics_pvs import AlarmSeverity +light.y.tolerated_alarm = AlarmSeverity.MAJOR + ## Goniometer Stack gonio = GoniometerStack('XF:17IDC-ES:FMX{Gon:1', name='gonio') @@ -1124,8 +1128,10 @@ def fmx_flux_reference(slit1GapList = [2000, 1000, 600, 400], slit1GapDefault = ]) # Put in diode - yield from bps.mv(light.y,govPositionGet('li', 'Diode')) + yield from bps.mv(light.y, + govs.gov.Robot.dev.li.target_Diode.get()) + # Open BCU shutter yield from bps.mv(shutter_bcu.open, 1) time.sleep(1) @@ -1149,8 +1155,9 @@ def fmx_flux_reference(slit1GapList = [2000, 1000, 600, 400], slit1GapDefault = yield from bps.mv(shutter_bcu.close, 1) # Retract diode - yield from bps.mv(light.y,govPositionGet('li', 'In')) - + yield from bps.mv(light.y, + govs.gov.Robot.dev.li.target_In.get()) + # Set previous beam transmission if transSet != 'None': if transSet in ['All', 'RI']: From b397f7290f0f34b5a21a9bf2f0290090affddb02 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Fri, 3 Mar 2023 12:12:39 -0500 Subject: [PATCH 23/79] remove homing pins after sample mount on FMX * due to unreliable gpy homing --- embl_robot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embl_robot.py b/embl_robot.py index 653f0daf..bf34941f 100644 --- a/embl_robot.py +++ b/embl_robot.py @@ -262,9 +262,9 @@ def mount(self, gov_robot, puckPos,pinPos,sampID,**kwargs): else: robotStatus = beamline_support.get_any_epics_pv("SW:RobotState","VAL") if (robotStatus != "Ready"): - if (daq_utils.beamline == "fmx"): - daq_macros.homePins() - time.sleep(3.0) + #if (daq_utils.beamline == "fmx"): + # daq_macros.homePins() + # time.sleep(3.0) gov_status = gov_lib.setGovRobot(gov_robot, 'SE') if not gov_status.success: return MOUNT_FAILURE From 6a741839bf70dea655b6f2a547b83c03cf191737 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Thu, 6 Jul 2023 11:29:47 -0400 Subject: [PATCH 24/79] remove commented-out code for homing pins on FMX --- embl_robot.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/embl_robot.py b/embl_robot.py index bf34941f..35903b36 100644 --- a/embl_robot.py +++ b/embl_robot.py @@ -262,9 +262,6 @@ def mount(self, gov_robot, puckPos,pinPos,sampID,**kwargs): else: robotStatus = beamline_support.get_any_epics_pv("SW:RobotState","VAL") if (robotStatus != "Ready"): - #if (daq_utils.beamline == "fmx"): - # daq_macros.homePins() - # time.sleep(3.0) gov_status = gov_lib.setGovRobot(gov_robot, 'SE') if not gov_status.success: return MOUNT_FAILURE From 5f219dcd7875da99f0b69de0e4c5561a4b49f834 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Mon, 15 May 2023 13:25:19 -0400 Subject: [PATCH 25/79] move omega to 0 before SA->SE on FMX * do this right before governor SA -> SE to prevent spurious changes to omega during windback to 0 --- embl_robot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embl_robot.py b/embl_robot.py index 35903b36..69b7ce2f 100644 --- a/embl_robot.py +++ b/embl_robot.py @@ -358,6 +358,8 @@ def preUnmount(self, gov_robot, puckPos,pinPos,sampID): #will somehow know where detDist = beamline_lib.motorPosFromDescriptor("detectorDist") if (detDist Date: Fri, 21 Jul 2023 11:00:58 -0400 Subject: [PATCH 27/79] Removed hard coded return statements to properly return robot failures --- robot_lib.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/robot_lib.py b/robot_lib.py index 0aed4eb2..af0e6b3b 100644 --- a/robot_lib.py +++ b/robot_lib.py @@ -26,21 +26,22 @@ def mountRobotSample(gov_robot, puck_pos, pin_pos, samp_id, **kwargs): if status != MOUNT_STEP_SUCCESSFUL: return status status = robot.postMount(gov_robot, puck_pos, pin_pos, samp_id) - return MOUNT_SUCCESSFUL # TODO hard-coded for testing + return status def unmountRobotSample(gov_robot, puck_pos, pin_pos, samp_id): status = robot.preUnmount(gov_robot, puck_pos, pin_pos, samp_id) if status != UNMOUNT_STEP_SUCCESSFUL: return status + + status = UNMOUNT_SUCCESSFUL if robot.control_type() == "Bluesky": RE(robot.unmount(gov_robot, puck_pos, pin_pos, samp_id)) - else: - robot.unmount(gov_robot, puck_pos, pin_pos, samp_id) + if isinstance(robot, OphydRobot): status = robot.check_sample_mounted(mount=False, puck_pos=puck_pos, pin_pos=pin_pos) else: - status = UNMOUNT_STEP_SUCCESSFUL # TODO assume embl robot is successful - return UNMOUNT_SUCCESSFUL # TODO hard-coded for testing + status = robot.unmount(gov_robot, puck_pos, pin_pos, samp_id) + return status def finish(): From 64425a2ff92288aa04b06134298f0e168acaf99d Mon Sep 17 00:00:00 2001 From: Shekar V Date: Fri, 21 Jul 2023 14:34:16 -0400 Subject: [PATCH 28/79] Removed unecessary assignment --- robot_lib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/robot_lib.py b/robot_lib.py index af0e6b3b..d9c4bff6 100644 --- a/robot_lib.py +++ b/robot_lib.py @@ -33,7 +33,6 @@ def unmountRobotSample(gov_robot, puck_pos, pin_pos, samp_id): if status != UNMOUNT_STEP_SUCCESSFUL: return status - status = UNMOUNT_SUCCESSFUL if robot.control_type() == "Bluesky": RE(robot.unmount(gov_robot, puck_pos, pin_pos, samp_id)) From b89c788e377e933751481170d413ce625a0ea9a0 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 25 Jul 2023 11:41:24 -0400 Subject: [PATCH 29/79] Revert "comment out ISPyB calls to database" This reverts commit 807573e9b782eddaed9cb6f1a5b2c07a7a8e8f6b. --- ispybLib.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/ispybLib.py b/ispybLib.py index bc0fd2ed..567ef50c 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -20,7 +20,7 @@ #request_dicts = lsdb2.getColRequestsByTimeInterval('2018-02-14T00:00:00','2018-02-15T00:00:00') # Connect to ISPyB, get the relevant data area objects -conn = ispyb.open(credentials=conf_file) +conn = ispyb.open(conf_file) core = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.CORE, conn) mxacquisition = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXACQUISITION, conn) mxprocessing = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXPROCESSING, conn) @@ -57,7 +57,6 @@ def maxVisitNumfromProposal(propNum): def createPerson(firstName,lastName,loginName): - return params = core.get_person_params() params['given_name'] = firstName params['family_name'] = lastName @@ -67,7 +66,6 @@ def createPerson(firstName,lastName,loginName): def createProposal(propNum,PI_login="boaty"): - return pid = personIdFromLogin(PI_login) if (pid == 0): createPerson("Not","Sure",PI_login) @@ -82,7 +80,6 @@ def createProposal(propNum,PI_login="boaty"): cnx.commit() #not sure why I needed to do this. Maybe mistake in stored proc? def createVisitName(propNum): # this is for the GUI to know what a datapath would be in row_clicked - return logger.info("creating visit Name for propnum " + str(propNum)) propID = proposalIdFromProposal(propNum) if (propID == 0): #proposal doesn't exist, just create and assign to boaty @@ -98,7 +95,6 @@ def createVisitName(propNum): # this is for the GUI to know what a datapath woul def createVisit(propNum): - return visitName, newVisitNum = createVisitName(propNum) personID = personIdFromProposal(propNum) params = core.get_session_for_proposal_code_number_params() @@ -167,7 +163,6 @@ def createVisit(propNum): return visitName def addPersonToProposal(personLogin,propNum): - return personID = personIdFromLogin(personLogin) if (personID == 0): createPerson("Not","Sure",personLogin) @@ -185,7 +180,6 @@ def addPersonToProposal(personLogin,propNum): def insertPlotResult(dc_id,imageNumber,spotTotal,goodBraggCandidates,method2Res,totalIntegratedSignal): - return params = mxprocessing.get_quality_indicators_params() params['datacollectionid'] = dc_id params['imageNumber'] = imageNumber @@ -198,7 +192,6 @@ def insertPlotResult(dc_id,imageNumber,spotTotal,goodBraggCandidates,method2Res, def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None): #xmlfilename for fastDP #keep in mind that request type can be standard and result type be fastDP - multiple results per req. - return try: sessionid = core.retrieve_visit_id(visitName) @@ -337,7 +330,6 @@ def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None def createDataCollection(directory, filePrefix, jpegImageFilename, params, request_obj, sessionid): - return params['starttime'] = datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S') params['endtime'] = datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S') dcg_id = mxacquisition.insert_data_collection_group(list(params.values())) @@ -388,7 +380,6 @@ def createDataCollection(directory, filePrefix, jpegImageFilename, params, reque # params['overlap'] = 89.0 def insertRasterResult(request,visitName): - return try: sessionid = core.retrieve_visit_id(visitName) except ISPyBNoResultException as e: From 3ee42283ef728caf33ea9aa2875e76d7e7459497 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 25 Jul 2023 11:01:16 -0400 Subject: [PATCH 30/79] Revert "fix for proposal name while ISPyB is down" This reverts commit ce27170e5d2e9bc511b17ac18760e507bb1013bd. --- daq_utils.py | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/daq_utils.py b/daq_utils.py index 6dca8948..169b4f26 100644 --- a/daq_utils.py +++ b/daq_utils.py @@ -364,27 +364,19 @@ def readPVDesc(): counter_inf = line.split() counter_dict[counter_inf[1]] = beamline_designation + counter_inf[0] -def createVisitNameRaw(proposalName, maxNumber=None): - if maxNumber: - number = maxNumber + 1 - else: - number = 1 - return f'mx{proposalName}-{number}', number - -def createVisitName(proposalName, maxNumber=None): - return createVisitNameRaw(proposalName, maxNumber) - -def setProposalID(proposalID,createVisit=True): # TODO JA proposalID implies a database ID, which it is not (just proposal number). Misleading - if (getProposalID() != proposalID): #proposalID changed - create a new visit. - logger.info("you changed proposals! " + str(proposalID)) - logger.info('ignoring createVisit for now - ISPyB required to properly account for visit numbers') - try: - visitName, visitNum = createVisitName(proposalID) - db_lib.setBeamlineConfigParam(beamline,"proposal",proposalID) - except Exception as e: - visitName = "999999-1234" - logger.error("error in set proposal. Error: %s" % e) - setVisitName(visitName) +def setProposalID(proposalID,createVisit=True): + if (getProposalID() != proposalID): #proposalID changed - create a new visit. + logger.info("you changed proposals! " + str(proposalID)) + try: + if (createVisit): + visitName = ispybLib.createVisit(proposalID) + db_lib.setBeamlineConfigParam(beamline,"proposal",proposalID) + else: + visitName, visitNum = ispybLib.createVisitName(proposalID) + except Exception as e: + visitName = "999999-1234" + logger.error("ispyb error in set proposal. Error: %s" % e) + setVisitName(visitName) def getProposalID(): return getBlConfig("proposal") From e736700509f5b06214aeec5c6e1c1241390ab71d Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 25 Jul 2023 11:08:05 -0400 Subject: [PATCH 31/79] use config from /etc/ispyb --- ispybLib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ispybLib.py b/ispybLib.py index 567ef50c..24eb03b2 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -14,7 +14,7 @@ #12/19 - I'm leaving all commented lines alone on this. Karl Levik, DLS, is an immense help with this. -conf_file = os.environ["CONFIGDIR"] + "ispybConfig.cfg" +conf_file = "/etc/ispyb/ispybConfig.cfg" visit = 'mx99999-1' # Get a list of request dicts #request_dicts = lsdb2.getColRequestsByTimeInterval('2018-02-14T00:00:00','2018-02-15T00:00:00') From f3653aeef411ec4e1ba21422b44a59e9bddac503 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 25 Jul 2023 11:21:37 -0400 Subject: [PATCH 32/79] use mysql connector object from ISPyB connection --- ispybLib.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ispybLib.py b/ispybLib.py index 24eb03b2..d0fb7a45 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -8,7 +8,6 @@ import db_lib import det_lib import time -import mysql.connector import logging logger = logging.getLogger(__name__) @@ -25,7 +24,7 @@ mxacquisition = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXACQUISITION, conn) mxprocessing = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXPROCESSING, conn) mxscreening = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXSCREENING, conn) -cnx = mysql.connector.connect(user='ispyb_api', password=os.environ['ISPYB_PASSWORD'],host='ispyb-db.nsls2.bnl.local',database='ispyb') +cnx = conn.conn cursor = cnx.cursor() beamline = os.environ["BEAMLINE_ID"] From f1fd6226e6085aff6f2f3e50c350d038acbd644c Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 25 Jul 2023 14:35:35 -0400 Subject: [PATCH 33/79] fix missing parenthesis --- gui/control_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/control_main.py b/gui/control_main.py index 02ec4aaa..a233d85e 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -3111,7 +3111,7 @@ def takeRasterSnapshot(self, rasterReq): reqID=rasterReq["uid"], rasterHeatJpeg=jpegImageFilename, ) - self.send_to_server(f"ispybLib.insertRasterResult('{rasterReq['uid']}', '{visitName}'") + self.send_to_server(f"ispybLib.insertRasterResult('{rasterReq['uid']}', '{visitName}')") def reFillPolyRaster(self): rasterEvalOption = str(self.rasterEvalComboBox.currentText()) From 19ddc5b412b307f56f629447e42f14b7ebd31fe2 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Mon, 7 Aug 2023 10:38:04 -0400 Subject: [PATCH 34/79] Revert "all server and GUI instances will be run from lsdc__prod" This reverts commit deec17872792d9675a78e85cfc2b81a385300f4e. * un-doing my original plan to separate LSDC code into prod and dev locations. will keep running code from lsdc_ for now --- bin/lsdcGui_amx | 2 +- bin/lsdcGui_fmx | 2 +- bin/lsdcGui_nyx | 2 +- bin/lsdcRemote_amx.cmd | 2 +- bin/lsdcRemote_fmx.cmd | 2 +- bin/lsdcServer_amx.cmd | 2 +- bin/lsdcServer_fmx.cmd | 2 +- bin/lsdcServer_nyx.cmd | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/lsdcGui_amx b/bin/lsdcGui_amx index c684d14a..b39909a2 100755 --- a/bin/lsdcGui_amx +++ b/bin/lsdcGui_amx @@ -1,6 +1,6 @@ export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_amx_prod +export LSDCHOME=${PROJDIR}lsdc_amx export PATH=/usr/local/bin:/usr/bin:/bin export PYTHONPATH=".:${CONFIGDIR}:/opt/dectris/albula/4.0/python:${LSDCHOME}" diff --git a/bin/lsdcGui_fmx b/bin/lsdcGui_fmx index 159fdb4c..e028aee7 100755 --- a/bin/lsdcGui_fmx +++ b/bin/lsdcGui_fmx @@ -1,6 +1,6 @@ export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_fmx_prod +export LSDCHOME=${PROJDIR}lsdc_fmx export PATH=/usr/local/bin:/usr/bin:/bin export PYTHONPATH=".:${CONFIGDIR}:/opt/dectris/albula/4.0/python:${LSDCHOME}" diff --git a/bin/lsdcGui_nyx b/bin/lsdcGui_nyx index a0aa62ac..55d2122d 100755 --- a/bin/lsdcGui_nyx +++ b/bin/lsdcGui_nyx @@ -1,6 +1,6 @@ export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_nyx_prod +export LSDCHOME=${PROJDIR}lsdc_nyx export EPICS_CA_AUTO_ADDR_LIST=NO export EPICS_CA_ADDR_LIST=10.67.147.255 diff --git a/bin/lsdcRemote_amx.cmd b/bin/lsdcRemote_amx.cmd index ee8f0626..13f770b7 100755 --- a/bin/lsdcRemote_amx.cmd +++ b/bin/lsdcRemote_amx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_amx_prod +export LSDCHOME=${PROJDIR}lsdc_amx export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env_amx.txt diff --git a/bin/lsdcRemote_fmx.cmd b/bin/lsdcRemote_fmx.cmd index a892611b..052d8241 100755 --- a/bin/lsdcRemote_fmx.cmd +++ b/bin/lsdcRemote_fmx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_fmx_prod +export LSDCHOME=${PROJDIR}lsdc_fmx export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env.txt diff --git a/bin/lsdcServer_amx.cmd b/bin/lsdcServer_amx.cmd index 24fc70e6..64e6d4ed 100755 --- a/bin/lsdcServer_amx.cmd +++ b/bin/lsdcServer_amx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_amx_prod +export LSDCHOME=${PROJDIR}lsdc_amx export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env_amx.txt diff --git a/bin/lsdcServer_fmx.cmd b/bin/lsdcServer_fmx.cmd index 096bdb41..59e63cfb 100755 --- a/bin/lsdcServer_fmx.cmd +++ b/bin/lsdcServer_fmx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_fmx_prod +export LSDCHOME=${PROJDIR}lsdc_fmx export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env.txt diff --git a/bin/lsdcServer_nyx.cmd b/bin/lsdcServer_nyx.cmd index e46707bf..b06b291b 100755 --- a/bin/lsdcServer_nyx.cmd +++ b/bin/lsdcServer_nyx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_nyx_prod +export LSDCHOME=${PROJDIR}lsdc_nyx export EPICS_CA_AUTO_ADDR_LIST=NO export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin From 41d75177ef503ed3a47052be1c35651f1444783b Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Mon, 14 Aug 2023 12:02:09 -0400 Subject: [PATCH 35/79] initial release notes --- docs/source/developer_notes/release_notes.rst | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/source/developer_notes/release_notes.rst b/docs/source/developer_notes/release_notes.rst index 54c0b40f..ba45ba1d 100644 --- a/docs/source/developer_notes/release_notes.rst +++ b/docs/source/developer_notes/release_notes.rst @@ -2,6 +2,38 @@ Release History ================= +2.0.2 (2023-09-xx, a.k.a. 2023-3) +================================= + +Fixes and other changes +----------------------- + +* GUI + + * GUI code split up into its own module + * Fixes for lifetime calculation + * Improve Albula handling + +* Server and GUI security improvements + + * Use external file to determine visit directory + * Use same visit directory location for server and GUI, and force all files acquired by LSDC to be written to that directory + * Enforce GUI and server starting in the visit directory + * Ensure server is started as one of the known LSDC service users (as opposed to a staff member in n2sn-inststaff-) + + +* Improved synchronization of detector and governor +* Re-enable ISPyB storage of data collections and processing results +* Standardize handling of FMX towards AMX when mounting a sample +* Move storage of raster results in ISPyB onto server, to remove ISPyB dependence in GUI + +* NYX-specific (2023-2-nyx) + + * GUI improvements + + * General layout + * NYX-specific changes + 2.0.1 (2023-04-20, a.k.a. 2023-2) ================================= From 97eb6f0518ac7f8f1bafdb89c6cf4ceb8dabc210 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Fri, 19 May 2023 16:13:55 -0400 Subject: [PATCH 36/79] Removed replaceLine function removed traceback Fixed run_rd3d function call --- raddoseLib.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/raddoseLib.py b/raddoseLib.py index 84449c45..5d4df1d2 100755 --- a/raddoseLib.py +++ b/raddoseLib.py @@ -8,18 +8,10 @@ import os.path import os import logging -import traceback logger = logging.getLogger(__name__) #12/19 - See Martin about this code! - -def replaceLine(file,searchExp,replaceExp): - for line in fileinput.input(file, inplace=1): - if searchExp in line: - line = replaceExp - sys.stdout.write(line) - def replaceLines(filename, replacementStrings): lines = [] with open(filename, 'r') as f: @@ -141,7 +133,7 @@ def rd3d_calc(flux=3.5e12, energy=12.66, replaceLines(inputFilePath, replacementStrings) - out = run_rd3d(inputFilePath, outputFilePath) + out = run_rd3d(inputFilePath) if verbose: logger.info(out) @@ -295,7 +287,6 @@ def fmx_expTime(avg_dwd = 10, #Default of 10MGy dose1s = rd3d_out['DWD'].item() # .item() to convert 1d array to scalar logger.info('Average Diffraction Weighted Dose for 1s exposure = {:f} MGy'.format(dose1s)) except Exception as e: - traceback.print_exc() logger.error(f'Exception in rd3d calc: {e}') dose1s = 0 From 26d3de0bb580b82ec254c826f35283fa4b3291b7 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Thu, 29 Jun 2023 17:29:03 -0400 Subject: [PATCH 37/79] Cleanup and formatting --- raddoseLib.py | 304 +++++++++++++++++++++++++++----------------------- 1 file changed, 166 insertions(+), 138 deletions(-) diff --git a/raddoseLib.py b/raddoseLib.py index 5d4df1d2..4c1ef5a4 100755 --- a/raddoseLib.py +++ b/raddoseLib.py @@ -8,53 +8,80 @@ import os.path import os import logging + logger = logging.getLogger(__name__) -#12/19 - See Martin about this code! +# 12/19 - See Martin about this code! + def replaceLines(filename, replacementStrings): lines = [] - with open(filename, 'r') as f: + with open(filename, "r") as f: for line in f.readlines(): for text_to_search, replacement_text in replacementStrings.items(): if text_to_search in line: line = replacement_text break lines.append(line) - - with open(filename, 'w') as f: + + with open(filename, "w") as f: f.writelines(lines) - + def run_rd3d(inputFileName): - prc = subprocess.Popen(["java", "-jar", os.environ['CONFIGDIR'] + "/raddose3d.jar", "-i", inputFileName, "-p", f"rd3d/rd3d_{os.getpid()}_"], + prc = subprocess.Popen( + [ + "java", + "-jar", + os.environ["CONFIGDIR"] + "/raddose3d.jar", + "-i", + inputFileName, + "-p", + f"rd3d/rd3d_{os.getpid()}_", + ], stdout=subprocess.PIPE, - universal_newlines=True) + universal_newlines=True, + ) out = prc.communicate()[0] return out -def rd3d_calc(flux=3.5e12, energy=12.66, - beamType='GAUSSIAN', fwhmX=2, fwhmY=1, collimationX=10, collimationY=10, - wedge=0, exposureTime=1, - translatePerDegX=0, translatePerDegY=0, translatePerDegZ=0, - startOffsetX=0, startOffsetY=0, startOffsetZ=0, - dimX=20, dimY=20, dimZ=20, - pixelsPerMicron=2, angularResolution=2, - templateFileName = os.environ["CONFIGDIR"] + '/rd3d_input_template.txt', - verbose=True, - ): + +def rd3d_calc( + flux=3.5e12, + energy=12.66, + beamType="GAUSSIAN", + fwhmX=2, + fwhmY=1, + collimationX=10, + collimationY=10, + wedge=0, + exposureTime=1, + translatePerDegX=0, + translatePerDegY=0, + translatePerDegZ=0, + startOffsetX=0, + startOffsetY=0, + startOffsetZ=0, + dimX=20, + dimY=20, + dimZ=20, + pixelsPerMicron=2, + angularResolution=2, + templateFileName=os.environ["CONFIGDIR"] + "/rd3d_input_template.txt", + verbose=True, +): """ RADDOSE3D dose estimate - + This version calculates dose values for an average protein crystal. The estimates need to be adjusted proportionally for a crystal if it is more/less sensitive. - + All paramaters listed below can be set. If they are not set explicitly, RADDOSE3D will use the listed default value. - + A complete manual with explanations is available at https://github.com/GarmanGroup/RADDOSE-3D/blob/master/doc/user-guide.pdf - + Photon flux [ph/s]: flux=3.5e12 Photon energy [keV]: energy=12.66, Beamtype (GAUSSIAN | TOPHAT): beamType='GAUSSIAN' @@ -76,24 +103,24 @@ def rd3d_calc(flux=3.5e12, energy=12.66, Pixels per micron: pixelsPerMicron=2 Angular resolution: angularResolution=2 Template file (in 'rd3d' subdir of active notebook): templateFileName = 'rd3d_input_template.txt' - + Return value is a structured numpy array. You can use it for follow-up calculations of the results returned by RADDOSE3D in "output-Summary.csv". Call the return variable to find the field names. - + Examples: rd3d_out = rd3d_calc(flux=1.35e12, exposuretime=0.01, dimx=1, dimy=1, dimz=1) rd3d_calc(flux=1e12, energy=12.7, fwhmX=3, fwhmY=5, collimationX=9, collimationY=15, wedge=180, exposureTime=8, translatePerDegX=0, translatePerDegY=0.27, startOffsetY=-25, dimX=3, dimY=80, dimZ=3, pixelsPerMicron=0.5, angularResolution=2, verbose=False) - + Setup: * rd3d_input_template.txt and raddose.jar in subdir rd3d/ * PDB file 2vb1.pdb in notebook dir 2vb1.pdb rd3d/raddose.jar rd3d/rd3d_input_template.txt - + Todo: * Cannot call PDB file in subdir or active dir, i.e. only 'PDB 2vb1.pdb' works in rd3d_input_template.txt * run_rd3d() with subdir option @@ -103,216 +130,217 @@ def rd3d_calc(flux=3.5e12, energy=12.66, * Keep template file as option? Then it should be in same dir as PDB file (notebook dir) * Protein option PDB (on xf17id1 cannot reach PDB URL) or JJDUMMY (how to cite?) """ - + rd3d_dir = "rd3d" inputFileName = f"rd3d_{os.getpid()}_input.txt" outputFileName = f"rd3d_{os.getpid()}_Summary.csv" - templateFilePath=os.path.join(rd3d_dir,templateFileName) - inputFilePath=os.path.join(rd3d_dir,inputFileName) - outputFilePath=os.path.join(rd3d_dir,outputFileName) - + templateFilePath = os.path.join(rd3d_dir, templateFileName) + inputFilePath = os.path.join(rd3d_dir, inputFileName) + outputFilePath = os.path.join(rd3d_dir, outputFileName) + copyfile(templateFilePath, inputFilePath) replacementStrings = { - "FLUX":'FLUX {:.2e}\n'.format(flux), - "ENERGY":'ENERGY {:.2f}\n'.format(energy), - "TYPE GAUSSIAN":'TYPE {:s}\n'.format(beamType), - "FWHM":'FWHM {:.1f} {:.1f}\n'.format(fwhmX,fwhmY), - "COLLIMATION":'COLLIMATION RECTANGULAR {:.1f} {:.1f}\n'.format(collimationX,collimationY), - "WEDGE":'WEDGE 0 {:0.1f}\n'.format(wedge), - "EXPOSURETIME":'EXPOSURETIME {:0.3f}\n'.format(exposureTime), - "TRANSLATEPERDEGREE": - 'TRANSLATEPERDEGREE {:0.4f} {:0.4f} {:0.4f}\n'.format(translatePerDegX,translatePerDegY,translatePerDegZ), - "DIMENSION":'DIMENSION {:0.1f} {:0.1f} {:0.1f}\n'.format(dimX,dimY,dimZ), - "PIXELSPERMICRON":'PIXELSPERMICRON {:0.1f}\n'.format(pixelsPerMicron), - "ANGULARRESOLUTION":'ANGULARRESOLUTION {:0.1f}\n'.format(angularResolution), - "STARTOFFSET": - 'STARTOFFSET {:f} {:f} {:f}\n'.format(startOffsetX,startOffsetY,startOffsetZ) + "FLUX": f"FLUX {flux:.2e}\n", + "ENERGY": f"ENERGY {energy:.2f}\n", + "TYPE GAUSSIAN": f"TYPE {beamType}\n", + "FWHM": f"FWHM {fwhmX:.1f} {fwhmY:.1f}\n", + "COLLIMATION": f"COLLIMATION RECTANGULAR {collimationX:.1f} {collimationY:.1f}\n", + "WEDGE": f"WEDGE 0 {wedge:0.1f}\n", + "EXPOSURETIME": f"EXPOSURETIME {exposureTime:0.3f}\n", + "TRANSLATEPERDEGREE": f"TRANSLATEPERDEGREE {translatePerDegX:0.4f} {translatePerDegY:0.4f} {translatePerDegZ:0.4f}\n", + "DIMENSION": f"DIMENSION {dimX:0.1f} {dimY:0.1f} {dimZ:0.1f}\n", + "PIXELSPERMICRON": f"PIXELSPERMICRON {pixelsPerMicron:0.1f}\n", + "ANGULARRESOLUTION": f"ANGULARRESOLUTION {angularResolution:0.1f}\n", + "STARTOFFSET": f"STARTOFFSET {startOffsetX} {startOffsetY} {startOffsetZ}\n", } replaceLines(inputFilePath, replacementStrings) - + out = run_rd3d(inputFilePath) if verbose: logger.info(out) - - rd3d_out = np.genfromtxt(outputFilePath, delimiter=',', names=True) + + rd3d_out = np.genfromtxt(outputFilePath, delimiter=",", names=True) logger.info("\n=== rd3d_calc summary ===") # append_fields has issues with 1d arrays, use reshape() and [] to make len() work on size 1 array: # https://stackoverflow.com/questions/53137822/adding-a-field-to-a-structured-numpy-array-4 rd3d_out = rd3d_out.reshape(1) - logger.info("Diffraction weighted dose = " + "%.3f" % rd3d_out['DWD'] + " MGy") - logger.info("Max dose = " + "%.3f" % rd3d_out['Max_Dose'] + " MGy") - if rd3d_out['DWD']: - t2gl = exposureTime * 30 / rd3d_out['DWD'] # Time to Garman limit based on diffraction weighted dose + logger.info("Diffraction weighted dose = " + "%.3f" % rd3d_out["DWD"] + " MGy") + logger.info("Max dose = " + "%.3f" % rd3d_out["Max_Dose"] + " MGy") + if rd3d_out["DWD"]: + t2gl = ( + exposureTime * 30 / rd3d_out["DWD"] + ) # Time to Garman limit based on diffraction weighted dose else: t2gl = 0 - rd3d_out = rfn.append_fields(rd3d_out,'t2gl',[t2gl],usemask=False) - logger.info("Time to Garman limit = " + "%.3f" % rd3d_out['t2gl'] + " s") - + rd3d_out = rfn.append_fields(rd3d_out, "t2gl", [t2gl], usemask=False) + logger.info("Time to Garman limit = " + "%.3f" % rd3d_out["t2gl"] + " s") + return rd3d_out + # ## Experiment time to reach 10 MGy dose -# +# # ### Inputs: # * Flux: From flux-at-sample PV # * Beam size: For now, set by hand, or determine from PV, or get from get_beamsize(TBD) # * Crystal size: Match to beam size # * Vector length: Start with assumption, LSDC vector length is along X-axis. Update could use the real projections -# +# # * Exposure time = 1 s # * Translation per degree has to match total vector length -# +# # * RD3D output = AWD [MGy] -# +# # ### Input from LSDC: # * Protocol standard or vector # * Beamsize settings -# +# # ### Output: # * Experiment time [s] to Average Diffraction Weighted Dose = 10 MGy import epics -def fmx_expTime(avg_dwd = 10, #Default of 10MGy - beamsizeV = 1.0, beamsizeH = 2.0, - vectorL = 0, - energy = 12.66, - flux = -1, - wedge = 180, - verbose = False - ): + +def fmx_expTime( + avg_dwd=10, # Default of 10MGy + beamsizeV=1.0, + beamsizeH=2.0, + vectorL=0, + energy=12.66, + flux=-1, + wedge=180, + verbose=False, +): """ RD3D output = AWD [MGy] - - + + Parameters ---------- - + beamsizeV, beamsizeH: float Beam size (V, H) [um]. Default 1x2 (VxH). For now, set explicitly. - + vectorL: float Vector length [um]: Default 0 um. Make assumption that the vector is completely oriented along X-axis. - + energy: float Photon energy [keV]. Default 12.66 keV - + wedge: float Crystal rotation for complete experiment [deg]. Start at 0, end at wedge - + flux: float Flux at sample position [ph/s]. By default this value is copied from the beamline's flux-at-sample PV. Can also be set explicitly. - + verbose: boolean True: Print out RADDOSE3D output. Default False - - + + Internal parameters ------------------- - + Crystal size XYZ: Match to beam size perpendicular to (XZ), and to vector length along the rotation axis (Y) - - + + Returns ------- - + Experiment time [s] to Average Diffraction Weighted Dose = 10 MGy - - + + Todo ---- - + * Beamsize: Read from a beamsize PV, or get from a get_beamsize() function - Check CRL settings - Check BCU attenuator - If V1H1 then 10x10 (dep on E) - - If V0H0 then + - If V0H0 then - If BCU-Attn-T < 1.0 then 3x5 - If BCU-Attn-T = 1.0 then 1x2 * Vector length: Use the real projections """ - + # Beam size [um] fwhmX = beamsizeV fwhmY = beamsizeH - collimationX = 3*beamsizeV - collimationY = 3*beamsizeH - + collimationX = 3 * beamsizeV + collimationY = 3 * beamsizeH + # Set explicitly or use current flux if flux == -1: # Current flux [ph/s]: From flux-at-sample PV - fluxSample = epics.caget('XF:17IDA-OP:FMX{Mono:DCM-dflux-MA}') - logger.info('Flux at sample = {:.4g} ph/s'.format(fluxSample)) + fluxSample = epics.caget("XF:17IDA-OP:FMX{Mono:DCM-dflux-MA}") + logger.info("Flux at sample = {:.4g} ph/s".format(fluxSample)) else: - fluxSample = flux - + fluxSample = flux + # Crystal size [um]: Match to beam size in V, longer than vector in H # XYZ as defined by Raddose3D dimX = beamsizeV # Crystal dimension V [um] dimY = vectorL + beamsizeH # Crystal dimension H [um] dimZ = dimX # Crystal dimension along beam [um] - + # Start offset for horizontal vector to stay within crystal [um] startOffsetY = -vectorL / 2 - + # Exposure time [s] exposureTime = 1.0 - + # Avoid division by zero when calculating translatePerDegY - if wedge == 0: wedge = 1e-3 - + if wedge == 0: + wedge = 1e-3 + # Vector length [um]: Assume LSDC vector length is along X-axis (Raddose3D Y). # Translation per degree has to match total vector length translatePerDegY = vectorL / wedge try: - rd3d_out = rd3d_calc(flux=fluxSample, energy=energy, - fwhmX=fwhmX, fwhmY=fwhmY, - collimationX=collimationX, collimationY=collimationY, - wedge=wedge, - exposureTime=exposureTime, - translatePerDegY=translatePerDegY, - startOffsetY=startOffsetY, - pixelsPerMicron=5, angularResolution=1, - dimX=dimX, dimY=dimY, dimZ=dimZ, - verbose=verbose - ) - + rd3d_out = rd3d_calc( + flux=fluxSample, + energy=energy, + fwhmX=fwhmX, + fwhmY=fwhmY, + collimationX=collimationX, + collimationY=collimationY, + wedge=wedge, + exposureTime=exposureTime, + translatePerDegY=translatePerDegY, + startOffsetY=startOffsetY, + pixelsPerMicron=5, + angularResolution=1, + dimX=dimX, + dimY=dimY, + dimZ=dimZ, + verbose=verbose, + ) + logger.info("\n=== fmx_expTime summary ===") - dose1s = rd3d_out['DWD'].item() # .item() to convert 1d array to scalar - logger.info('Average Diffraction Weighted Dose for 1s exposure = {:f} MGy'.format(dose1s)) + dose1s = float(rd3d_out["DWD"].item()) # .item() to convert 1d array to scalar + logger.info( + "Average Diffraction Weighted Dose for 1s exposure = {:f} MGy".format( + dose1s + ) + ) except Exception as e: - logger.error(f'Exception in rd3d calc: {e}') - dose1s = 0 - + logger.error(f"Exception in rd3d calc: {e}") + dose1s = 0.0 + if dose1s > 0: - expTimeMGy = avg_dwd / dose1s # Experiment time to reach an average DWD (avg_dwd) + expTimeMGy = ( + avg_dwd / dose1s + ) # Experiment time to reach an average DWD (avg_dwd) else: - expTimeMGy = 0 - logger.info(f'Experiment time to reach an average diffraction weighted dose of {avg_dwd} MGy = {expTimeMGy} s') - - return expTimeMGy - + expTimeMGy = 0.0 + logger.info( + f"Experiment time to reach an average diffraction weighted dose of {avg_dwd} MGy = {expTimeMGy} s" + ) - -# Copy of Wuxian's 100 um vector (http://www.raddo.se/rd3d/job.php?u=16843&s=mLl5kFm5oXgRgpnf&id=16915) -#rd3d_calc(flux=1e12, energy=12.7, -# fwhmX=3, fwhmY=5, collimationX=9, collimationY=15, -# wedge=180, -# exposureTime=16, -# translatePerDegX=0, translatePerDegY=0.556, -# startOffsetY=-50, -# dimX=3, dimY=110, dimZ=3, -# pixelsPerMicron=0.5, angularResolution=2, -# ) - - -#fmx_expTime_to_10MGy(beamsizeV = 3.0, beamsizeH = 5.0, vectorL = 50, energy = 12.7, wedge = 180, flux = 1e12) - - -#fmx_expTime_to_10MGy(beamsizeV = 3.0, beamsizeH = 5.0, vectorL = 100, energy = 12.7, wedge = 180, flux = 1e12, verbose = True) + return expTimeMGy From 323d1c837e0a6805b249d5d587d4194e90727675 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Mon, 11 Sep 2023 09:03:48 -0400 Subject: [PATCH 38/79] Corrected docstring for fmx_flux_reference --- setenergy_lsdc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setenergy_lsdc.py b/setenergy_lsdc.py index 70d21127..21f68bd6 100644 --- a/setenergy_lsdc.py +++ b/setenergy_lsdc.py @@ -1071,7 +1071,7 @@ def slit1_flux_reference(flux_df,slit1Gap): def fmx_flux_reference(slit1GapList = [2000, 1000, 600, 400], slit1GapDefault = 1000, transSet='All'): """ Sets Slit 1 X gap and Slit 1 Y gap to a list of settings, - and returns flux reference values in a pandas DataFrame. + and stores flux reference values in a global pandas DataFrame. Parameters ---------- From ad1666969f355963cf96928ad6213ccf3df4cb3f Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Mon, 11 Sep 2023 13:51:54 -0400 Subject: [PATCH 39/79] further updates to release notes * latest merges into master - setE, lifetime fixes --- docs/source/developer_notes/release_notes.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/source/developer_notes/release_notes.rst b/docs/source/developer_notes/release_notes.rst index ba45ba1d..a64ec7c7 100644 --- a/docs/source/developer_notes/release_notes.rst +++ b/docs/source/developer_notes/release_notes.rst @@ -2,7 +2,7 @@ Release History ================= -2.0.2 (2023-09-xx, a.k.a. 2023-3) +2.0.2 (2023-09-12, a.k.a. 2023-3) ================================= Fixes and other changes @@ -26,8 +26,10 @@ Fixes and other changes * Re-enable ISPyB storage of data collections and processing results * Standardize handling of FMX towards AMX when mounting a sample * Move storage of raster results in ISPyB onto server, to remove ISPyB dependence in GUI +* Save FMX flux reference after energy change +* Make FMX behave more like AMX after sample mount - move omega to 0 before SA-SE governor change, do not home pins after sample mount -* NYX-specific (2023-2-nyx) +* NYX-specific (2023-2-nyx) - not merged into master due to significant differences * GUI improvements From 8e43bcb46b367ced165fd1515a4309708dd10ff9 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Mon, 11 Sep 2023 13:55:52 -0400 Subject: [PATCH 40/79] remove redundant line about FMX robot mount code changes --- docs/source/developer_notes/release_notes.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/developer_notes/release_notes.rst b/docs/source/developer_notes/release_notes.rst index a64ec7c7..9b3d18e1 100644 --- a/docs/source/developer_notes/release_notes.rst +++ b/docs/source/developer_notes/release_notes.rst @@ -27,7 +27,6 @@ Fixes and other changes * Standardize handling of FMX towards AMX when mounting a sample * Move storage of raster results in ISPyB onto server, to remove ISPyB dependence in GUI * Save FMX flux reference after energy change -* Make FMX behave more like AMX after sample mount - move omega to 0 before SA-SE governor change, do not home pins after sample mount * NYX-specific (2023-2-nyx) - not merged into master due to significant differences From 44ad6a44fff8eece4c4bbb4b16d933c64010efac Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Thu, 14 Sep 2023 14:36:03 -0400 Subject: [PATCH 41/79] fix ISPyB processing population error * taking first 256 characters of processing commandline to prevent the error "Data too long for column 'p_commandLine'" --- ispybLib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ispybLib.py b/ispybLib.py index 2fbcac78..97800d3f 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -203,6 +203,8 @@ def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None sample = request['sample'] # this needs to be created and linked to a DC group if (resultType == 'fastDP'): mx_data_reduction_dict = xml_file_to_dict(xmlFileName) + comm = mx_data_reduction_dict['AutoProcProgramContainer']['AutoProcProgram']['processingCommandLine'] + mx_data_reduction_dict['AutoProcProgramContainer']['AutoProcProgram']['processingCommandLine'] = comm[:255] (app_id, ap_id, scaling_id, integration_id) = mx_data_reduction_to_ispyb(mx_data_reduction_dict, dc_id, mxprocessing) mxprocessing.upsert_program_ex(program_id=app_id,status=1) From d550b9268b450e710195f70f8a2995a98570d607 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Thu, 14 Sep 2023 15:12:19 -0400 Subject: [PATCH 42/79] store last 255 characters of processing call * better to lose the first part of the fastDP call than more significant information at the end of the call --- ispybLib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ispybLib.py b/ispybLib.py index 97800d3f..62c5a4ea 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -204,7 +204,7 @@ def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None if (resultType == 'fastDP'): mx_data_reduction_dict = xml_file_to_dict(xmlFileName) comm = mx_data_reduction_dict['AutoProcProgramContainer']['AutoProcProgram']['processingCommandLine'] - mx_data_reduction_dict['AutoProcProgramContainer']['AutoProcProgram']['processingCommandLine'] = comm[:255] + mx_data_reduction_dict['AutoProcProgramContainer']['AutoProcProgram']['processingCommandLine'] = comm[(len(comm)-255:] (app_id, ap_id, scaling_id, integration_id) = mx_data_reduction_to_ispyb(mx_data_reduction_dict, dc_id, mxprocessing) mxprocessing.upsert_program_ex(program_id=app_id,status=1) From 59385115aefc85d1f5d11b1544225c3f6dca7ede Mon Sep 17 00:00:00 2001 From: Shekar V Date: Fri, 15 Sep 2023 11:08:57 -0400 Subject: [PATCH 43/79] Refactored refresh dewartree view: - Hide proposals that the user doesnt belong to - Split long method into smaller methods --- config_params.py | 6 + gui/dewar_tree.py | 328 +++++++++++++++++++++++----------------------- 2 files changed, 171 insertions(+), 163 deletions(-) diff --git a/config_params.py b/config_params.py index 5a984c22..54f5e891 100644 --- a/config_params.py +++ b/config_params.py @@ -1,4 +1,5 @@ from enum import Enum +import os, grp # BlConfig parameter variable names @@ -109,3 +110,8 @@ class RasterStatus(Enum): } LSDC_SERVICE_USERS = ("lsdc-amx", "lsdc-fmx", "lsdc-nyx") +IS_STAFF = ( + True + if os.environ["STAFF_GROUP"] in [grp.getgrgid(g).gr_name for g in os.getgroups()] + else False +) diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py index b0c05f9c..58bde972 100644 --- a/gui/dewar_tree.py +++ b/gui/dewar_tree.py @@ -1,23 +1,29 @@ import logging import typing - +import os, getpass from qtpy import QtCore, QtGui, QtWidgets from qtpy.QtCore import Qt - +import requests import daq_utils import db_lib -from config_params import DEWAR_SECTORS, PUCKS_PER_DEWAR_SECTOR, SAMPLE_TIMER_DELAY +from config_params import ( + DEWAR_SECTORS, + PUCKS_PER_DEWAR_SECTOR, + SAMPLE_TIMER_DELAY, + IS_STAFF, +) if typing.TYPE_CHECKING: from lsdcGui import ControlMain logger = logging.getLogger() -global sampleNameDict -sampleNameDict = {} +global sampleDict +sampleDict = {} global containerDict containerDict = {} +ICON = ":/trolltech/styles/commonstyle/images/file-16.png" class DewarTree(QtWidgets.QTreeView): @@ -33,6 +39,8 @@ def __init__(self, parent: "ControlMain"): # self.isExpanded = 1 self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.openMenu) + # Keeps track of whether the user is part of a proposal + self.proposal_membership = {} def openMenu(self, position): indexes = self.selectedIndexes() @@ -89,173 +97,174 @@ def keyPressEvent(self, event): def refreshTree(self): self.parent.dewarViewToggleCheckCB() + def set_mounted_sample(self, item): + # Formats the text of the item that is passed in as the mounted sample + item.setForeground(QtGui.QColor("red")) + font = QtGui.QFont() + font.setUnderline(True) + font.setItalic(True) + font.setOverline(True) + item.setFont(font) + def refreshTreeDewarView(self): - selectedIndex = None - mountedIndex = None - selectedSampleIndex = None puck = "" - collectionRunning = False self.model.clear() dewarContents = db_lib.getContainerByName( daq_utils.primaryDewarName, daq_utils.beamline )["content"] - for i in range(0, len(dewarContents)): # dewar contents is the list of puck IDs + for i, puck_id in enumerate( + dewarContents + ): # dewar contents is the list of puck IDs parentItem = self.model.invisibleRootItem() - if dewarContents[i] == "": - puck = "" - puckName = "" - else: - if dewarContents[i] not in containerDict: - puck = db_lib.getContainerByID(dewarContents[i]) - containerDict[dewarContents[i]] = puck - else: - puck = containerDict[dewarContents[i]] + puck = "" + puckName = "" + if puck_id: + puck = containerDict.get( + puck_id, + containerDict.setdefault(puck_id, db_lib.getContainerByID(puck_id)), + ) puckName = puck["name"] - index_s = "%d%s" % ( - (i) / self.pucksPerDewarSector + 1, - chr(((i) % self.pucksPerDewarSector) + ord("A")), - ) - item = QtGui.QStandardItem( - QtGui.QIcon(":/trolltech/styles/commonstyle/images/file-16.png"), - index_s + " " + puckName, - ) + sector, puck_pos = divmod(i, self.pucksPerDewarSector) + index_s = f"{sector+1}{chr(puck_pos + ord('A'))}" + item = QtGui.QStandardItem(QtGui.QIcon(ICON), f"{index_s} {puckName}") item.setData(puckName, 32) item.setData("container", 33) parentItem.appendRow(item) parentItem = item if puck != "" and puckName != "private": - puckContents = puck["content"] - puckSize = len(puckContents) - for j in range(0, len(puckContents)): # should be the list of samples - if puckContents[j] != "": - if puckContents[j] not in sampleNameDict: - sampleName = db_lib.getSampleNamebyID(puckContents[j]) - sampleNameDict[puckContents[j]] = sampleName - else: - sampleName = sampleNameDict[puckContents[j]] - position_s = str(j + 1) + "-" + sampleName - item = QtGui.QStandardItem( - QtGui.QIcon( - ":/trolltech/styles/commonstyle/images/file-16.png" - ), - position_s, - ) - item.setData( - puckContents[j], 32 - ) # just stuck sampleID there, but negate it to diff from reqID - item.setData("sample", 33) - if puckContents[j] == self.parent.mountedPin_pv.get(): - item.setForeground(QtGui.QColor("red")) - font = QtGui.QFont() - font.setItalic(True) - font.setOverline(True) - font.setUnderline(True) - item.setFont(font) - parentItem.appendRow(item) - if puckContents[j] == self.parent.mountedPin_pv.get(): - mountedIndex = self.model.indexFromItem(item) - if ( - puckContents[j] == self.parent.selectedSampleID - ): # looking for the selected item - logger.info("found " + str(self.parent.SelectedItemData)) - selectedSampleIndex = self.model.indexFromItem(item) - sampleRequestList = db_lib.getRequestsBySampleID( - puckContents[j] - ) - for k in range(len(sampleRequestList)): - if not ("protocol" in sampleRequestList[k]["request_obj"]): - continue - col_item = QtGui.QStandardItem( - QtGui.QIcon( - ":/trolltech/styles/commonstyle/images/file-16.png" - ), - sampleRequestList[k]["request_obj"]["file_prefix"] - + "_" - + sampleRequestList[k]["request_obj"]["protocol"], - ) - col_item.setData(sampleRequestList[k]["uid"], 32) - col_item.setData("request", 33) - col_item.setFlags( - Qt.ItemIsUserCheckable - | Qt.ItemIsEnabled - | Qt.ItemIsEditable - | Qt.ItemIsSelectable - ) - if sampleRequestList[k]["priority"] == 99999: - col_item.setCheckState(Qt.Checked) - col_item.setBackground(QtGui.QColor("green")) - selectedIndex = self.model.indexFromItem( - col_item - ) ##attempt to leave it on the request after collection - - collectionRunning = True - self.parent.refreshCollectionParams( - sampleRequestList[k], validate_hdf5=False - ) - elif sampleRequestList[k]["priority"] > 0: - col_item.setCheckState(Qt.Checked) - col_item.setBackground(QtGui.QColor("white")) - elif sampleRequestList[k]["priority"] < 0: - col_item.setCheckable(False) - col_item.setBackground(QtGui.QColor("cyan")) - else: - col_item.setCheckState(Qt.Unchecked) - col_item.setBackground(QtGui.QColor("white")) - item.appendRow(col_item) - if ( - sampleRequestList[k]["uid"] - == self.parent.SelectedItemData - ): # looking for the selected item, this is a request - selectedIndex = self.model.indexFromItem(col_item) - else: # this is an empty spot, no sample - position_s = str(j + 1) - item = QtGui.QStandardItem( - QtGui.QIcon( - ":/trolltech/styles/commonstyle/images/file-16.png" - ), - position_s, - ) - item.setData("", 32) - parentItem.appendRow(item) - self.setModel(self.model) - if selectedSampleIndex != None and collectionRunning == False: - self.setCurrentIndex(selectedSampleIndex) - if mountedIndex != None: - self.model.itemFromIndex(mountedIndex).setForeground( - QtGui.QColor("red") - ) - font = QtGui.QFont() - font.setUnderline(True) - font.setItalic(True) - font.setOverline(True) - self.model.itemFromIndex(mountedIndex).setFont(font) - self.parent.row_clicked(selectedSampleIndex) - elif selectedSampleIndex == None and collectionRunning == False: - if mountedIndex != None: - self.setCurrentIndex(mountedIndex) - self.model.itemFromIndex(mountedIndex).setForeground( - QtGui.QColor("red") - ) - font = QtGui.QFont() - font.setUnderline(True) - font.setItalic(True) - font.setOverline(True) - self.model.itemFromIndex(mountedIndex).setFont(font) - self.parent.row_clicked(mountedIndex) - else: - pass - if selectedIndex != None and collectionRunning == False: - self.setCurrentIndex(selectedIndex) - self.parent.row_clicked(selectedIndex) - if collectionRunning == True: - if mountedIndex != None: - self.setCurrentIndex(mountedIndex) + puckContents = puck.get("content", []) + self.add_samples_to_puck_tree(puckContents, parentItem) + self.setModel(self.model) if self.isExpanded: self.expandAll() else: self.collapseAll() self.scrollTo(self.currentIndex(), QtWidgets.QAbstractItemView.PositionAtCenter) + def add_samples_to_puck_tree(self, puckContents, parentItem: QtGui.QStandardItem): + # Method will attempt to add samples to the puck. If you don't belong to the proposal, + # it will not add samples and clear the puck information + selectedIndex = None + mountedIndex = None + selectedSampleIndex = None + collectionRunning = False + for j, sample_id in enumerate(puckContents): + if not sample_id: + # this is an empty spot, no sample + position_s = str(j + 1) + item = QtGui.QStandardItem(QtGui.QIcon(ICON), position_s) + item.setData("", 32) + parentItem.appendRow(item) + continue + + sample = sampleDict.get( + sample_id, + sampleDict.setdefault(sample_id, db_lib.getSampleByID(sample_id)), + ) + + if not self.is_proposal_member(sample["proposal"]) and not IS_STAFF: + # If the user is not part of the proposal or is not staff, don't fill tree + # Clear the puck information and don't make it selectable + parentItem.clearData() + parentItem.setText("") + current_flags = parentItem.flags() + parentItem.setFlags(current_flags & ~Qt.ItemFlag.ItemIsSelectable) # type: ignore + return + + position_s = f'{j+1}-{sample.get("name", "")}' + item = QtGui.QStandardItem( + QtGui.QIcon(ICON), + position_s, + ) + # just stuck sampleID there, but negate it to diff from reqID + item.setData(sample_id, 32) + item.setData("sample", 33) + if sample_id == self.parent.mountedPin_pv.get(): + self.set_mounted_sample(item) + parentItem.appendRow(item) + if sample_id == self.parent.mountedPin_pv.get(): + mountedIndex = self.model.indexFromItem(item) + # looking for the selected item + if sample_id == self.parent.selectedSampleID: + logger.info("found " + str(self.parent.SelectedItemData)) + selectedSampleIndex = self.model.indexFromItem(item) + sampleRequestList = db_lib.getRequestsBySampleID(sample_id) + for request in sampleRequestList: + if not ("protocol" in request["request_obj"]): + continue + col_item = self.create_request_item(request) + if request["priority"] == 99999: + selectedIndex = self.model.indexFromItem( + col_item + ) ##attempt to leave it on the request after collection + collectionRunning = True + item.appendRow(col_item) + if ( + request["uid"] == self.parent.SelectedItemData + ): # looking for the selected item, this is a request + selectedIndex = self.model.indexFromItem(col_item) + + current_index = None + if not collectionRunning: + if selectedSampleIndex: + current_index = selectedSampleIndex + elif mountedIndex: + current_index = mountedIndex + item = self.model.itemFromIndex(mountedIndex) + self.set_mounted_sample(item) + elif selectedIndex: + current_index = selectedIndex + elif collectionRunning and mountedIndex: + current_index = mountedIndex + + if current_index: + self.setCurrentIndex(current_index) + self.parent.row_clicked(current_index) + + def is_proposal_member(self, proposal) -> bool: + # Check if the user running LSDC is part of the sample's proposal + if proposal not in self.proposal_membership: + r = requests.get(f"{os.environ['NSLS_API_URL']}/proposal/{proposal}") + r.raise_for_status() + response = r.json() + if getpass.getuser() not in [ + user["username"] for user in response["users"] if "username" in user + ]: + self.proposal_membership[proposal] = False + else: + self.proposal_membership[proposal] = True + return self.proposal_membership[proposal] + + def create_request_item(self, request) -> QtGui.QStandardItem: + col_item = QtGui.QStandardItem( + QtGui.QIcon(ICON), + request["request_obj"]["file_prefix"] + + "_" + + request["request_obj"]["protocol"], + ) + col_item.setData(request["uid"], 32) + col_item.setData("request", 33) + col_item.setFlags( + Qt.ItemFlag.ItemIsUserCheckable # type:ignore + | Qt.ItemFlag.ItemIsEnabled + | Qt.ItemFlag.ItemIsEditable + | Qt.ItemFlag.ItemIsSelectable + ) + if request["priority"] == 99999: + col_item.setCheckState(Qt.CheckState.Checked) + col_item.setBackground(QtGui.QColor("green")) + self.parent.refreshCollectionParams(request, validate_hdf5=False) + elif request["priority"] > 0: + col_item.setCheckState(Qt.CheckState.Checked) + col_item.setBackground(QtGui.QColor("white")) + elif request["priority"] < 0: + col_item.setCheckable(False) + col_item.setBackground(QtGui.QColor("cyan")) + else: + col_item.setCheckState(Qt.CheckState.Unchecked) + col_item.setBackground(QtGui.QColor("white")) + return col_item + def refreshTreePriorityView( self, ): # "item" is a sample, "col_items" are requests which are children of samples. @@ -282,18 +291,13 @@ def refreshTreePriorityView( parentItem = self.model.invisibleRootItem() nodeString = str(db_lib.getSampleNamebyID(requestedSampleList[i])) item = QtGui.QStandardItem( - QtGui.QIcon(":/trolltech/styles/commonstyle/images/file-16.png"), + QtGui.QIcon(ICON), nodeString, ) item.setData(requestedSampleList[i], 32) item.setData("sample", 33) if requestedSampleList[i] == mountedPin: - item.setForeground(QtGui.QColor("red")) - font = QtGui.QFont() - font.setItalic(True) - font.setOverline(True) - font.setUnderline(True) - item.setFont(font) + self.set_mounted_sample(item) parentItem.appendRow(item) if requestedSampleList[i] == mountedPin: mountedIndex = self.model.indexFromItem(item) @@ -305,9 +309,7 @@ def refreshTreePriorityView( for k in range(len(self.orderedRequests)): if self.orderedRequests[k]["sample"] == requestedSampleList[i]: col_item = QtGui.QStandardItem( - QtGui.QIcon( - ":/trolltech/styles/commonstyle/images/file-16.png" - ), + QtGui.QIcon(ICON), self.orderedRequests[k]["request_obj"]["file_prefix"] + "_" + self.orderedRequests[k]["request_obj"]["protocol"], From a8fef96bd77de80e341ca0ed788806f2681c466d Mon Sep 17 00:00:00 2001 From: Shekar V Date: Fri, 15 Sep 2023 11:45:48 -0400 Subject: [PATCH 44/79] Set datapath during request creation to come from blconfig --- gui/control_main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 5bca9905..dbf2dd93 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -3676,7 +3676,7 @@ def editSampleRequestCB(self, singleRequest): singleRequest == 1 ): # a touch kludgy, but I want to be able to edit parameters for multiple requests w/o screwing the data loc info reqObj["file_prefix"] = str(self.dataPathGB.prefix_ledit.text()) - reqObj["basePath"] = str(self.dataPathGB.base_path_ledit.text()) + reqObj["basePath"] = getBlConfig("visitDirectory") reqObj["directory"] = str(self.dataPathGB.dataPath_ledit.text()) reqObj["file_number_start"] = int( self.dataPathGB.file_numstart_ledit.text() @@ -3934,9 +3934,9 @@ def addSampleRequestCB(self, rasterDef=None, selectedSampleID=None): reqObj["file_prefix"] = str( self.dataPathGB.prefix_ledit.text() + "_C" + str(i + 1) ) - reqObj["basePath"] = str(self.dataPathGB.base_path_ledit.text()) + reqObj["basePath"] = getBlConfig("visitDirectory") reqObj["directory"] = ( - str(self.dataPathGB.base_path_ledit.text()) + getBlConfig("visitDirectory") + "/" + str(daq_utils.getVisitName()) + "/" @@ -4060,7 +4060,7 @@ def addSampleRequestCB(self, rasterDef=None, selectedSampleID=None): ) reqObj["resolution"] = float(self.resolution_ledit.text()) reqObj["directory"] = ( - str(self.dataPathGB.base_path_ledit.text()) + getBlConfig("visitDirectory") + "/" + str(daq_utils.getVisitName()) + "/" @@ -4073,7 +4073,7 @@ def addSampleRequestCB(self, rasterDef=None, selectedSampleID=None): + str(samplePositionInContainer + 1) + "/" ) - reqObj["basePath"] = str(self.dataPathGB.base_path_ledit.text()) + reqObj["basePath"] = getBlConfig("visitDirectory") reqObj["file_prefix"] = str(self.dataPathGB.prefix_ledit.text()) reqObj["file_number_start"] = int( self.dataPathGB.file_numstart_ledit.text() From fd7310a98332a5026d06192553744d0aae56e428 Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Tue, 19 Sep 2023 14:17:37 -0400 Subject: [PATCH 45/79] Minimum working example for showing puck info --- gui/control_main.py | 8 ++++--- gui/dewar_tree.py | 52 ++++++++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 5bca9905..edd00918 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -201,7 +201,7 @@ def __init__(self): self.raddoseTimer.timeout.connect(self.spawnRaddoseThread) self.createSampleTab() - + self.userScreenDialog = UserScreenDialog(self) self.initCallbacks() if self.scannerType != "PI": self.motPos = { @@ -224,14 +224,13 @@ def __init__(self): if daq_utils.beamline == "nyx": # requires staffScreenDialog to be present self.staffScreenDialog.fastDPCheckBox.setDisabled(True) - self.dewarTree.refreshTreeDewarView() if self.mountedPin_pv.get() == "": mountedPin = db_lib.beamlineInfo(daq_utils.beamline, "mountedSample")[ "sampleID" ] self.mountedPin_pv.put(mountedPin) self.rasterExploreDialog = RasterExploreDialog() - self.userScreenDialog = UserScreenDialog(self) + self.detDistMotorEntry.getEntry().setText( self.detDistRBVLabel.getEntry().text() ) # this is to fix the current val being overwritten by reso @@ -241,6 +240,7 @@ def __init__(self): self.changeControlMasterCB(1) self.controlMasterCheckBox.setChecked(True) self.XRFInfoDict = self.parseXRFTable() # I don't like this + #self.dewarTree.refreshTreeDewarView() def setGuiValues(self, values): for item, value in values.items(): @@ -4624,6 +4624,8 @@ def row_clicked( self, index ): # I need "index" here? seems like I get it from selmod, but sometimes is passed selmod = self.dewarTree.selectionModel() + if not selmod: + return selection = selmod.selection() indexes = selection.indexes() if len(indexes) == 0: diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py index 58bde972..d745e204 100644 --- a/gui/dewar_tree.py +++ b/gui/dewar_tree.py @@ -35,7 +35,7 @@ def __init__(self, parent: "ControlMain"): self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) self.setAnimated(True) self.model = QtGui.QStandardItemModel() - self.model.itemChanged.connect(self.queueSelectedSample) + # self.isExpanded = 1 self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.openMenu) @@ -112,10 +112,10 @@ def refreshTreeDewarView(self): dewarContents = db_lib.getContainerByName( daq_utils.primaryDewarName, daq_utils.beamline )["content"] + parentItem = self.model.invisibleRootItem() for i, puck_id in enumerate( dewarContents ): # dewar contents is the list of puck IDs - parentItem = self.model.invisibleRootItem() puck = "" puckName = "" if puck_id: @@ -130,18 +130,18 @@ def refreshTreeDewarView(self): item.setData(puckName, 32) item.setData("container", 33) parentItem.appendRow(item) - parentItem = item if puck != "" and puckName != "private": puckContents = puck.get("content", []) - self.add_samples_to_puck_tree(puckContents, parentItem) - self.setModel(self.model) + self.add_samples_to_puck_tree(puckContents, item, index_s) + self.setModel(self.model) + self.model.itemChanged.connect(self.queueSelectedSample) if self.isExpanded: self.expandAll() else: self.collapseAll() self.scrollTo(self.currentIndex(), QtWidgets.QAbstractItemView.PositionAtCenter) - def add_samples_to_puck_tree(self, puckContents, parentItem: QtGui.QStandardItem): + def add_samples_to_puck_tree(self, puckContents, parentItem: QtGui.QStandardItem, index_label): # Method will attempt to add samples to the puck. If you don't belong to the proposal, # it will not add samples and clear the puck information selectedIndex = None @@ -162,13 +162,19 @@ def add_samples_to_puck_tree(self, puckContents, parentItem: QtGui.QStandardItem sampleDict.setdefault(sample_id, db_lib.getSampleByID(sample_id)), ) - if not self.is_proposal_member(sample["proposal"]) and not IS_STAFF: + #if not IS_STAFF and not self.is_proposal_member(sample["proposalID"]): + if not self.is_proposal_member(sample["proposalID"]): # If the user is not part of the proposal or is not staff, don't fill tree # Clear the puck information and don't make it selectable - parentItem.clearData() - parentItem.setText("") + # parentItem.clearData() + parentItem.setText(index_label) current_flags = parentItem.flags() parentItem.setFlags(current_flags & ~Qt.ItemFlag.ItemIsSelectable) # type: ignore + position_s = f'{j+1}-{sample.get("name", "")}' + item = QtGui.QStandardItem( + QtGui.QIcon(ICON), + position_s, + ) return position_s = f'{j+1}-{sample.get("name", "")}' @@ -227,12 +233,13 @@ def is_proposal_member(self, proposal) -> bool: r = requests.get(f"{os.environ['NSLS_API_URL']}/proposal/{proposal}") r.raise_for_status() response = r.json() - if getpass.getuser() not in [ + if "users" in response and getpass.getuser() in [ user["username"] for user in response["users"] if "username" in user ]: - self.proposal_membership[proposal] = False - else: self.proposal_membership[proposal] = True + else: + logger.info(f"Users not found in response: {response}") + self.proposal_membership[proposal] = False return self.proposal_membership[proposal] def create_request_item(self, request) -> QtGui.QStandardItem: @@ -362,16 +369,17 @@ def refreshTreePriorityView( self.expandAll() def queueSelectedSample(self, item): - reqID = str(item.data(32)) - checkedSampleRequest = db_lib.getRequestByID(reqID) # line not needed??? - if item.checkState() == Qt.Checked: - db_lib.updatePriority(reqID, 5000) - else: - db_lib.updatePriority(reqID, 0) - item.setBackground(QtGui.QColor("white")) - self.parent.treeChanged_pv.put( - self.parent.processID - ) # the idea is touch the pv, but have this gui instance not refresh + if item.data(33) == "request": + reqID = str(item.data(32)) + checkedSampleRequest = db_lib.getRequestByID(reqID) # line not needed??? + if item.checkState() == Qt.Checked: + db_lib.updatePriority(reqID, 5000) + else: + db_lib.updatePriority(reqID, 0) + item.setBackground(QtGui.QColor("white")) + self.parent.treeChanged_pv.put( + self.parent.processID + ) # the idea is touch the pv, but have this gui instance not refresh def queueAllSelectedCB(self): selmod = self.selectionModel() From e0d07ddf9645fdea0e0636b647fcbb5b09e52dab Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 11 Jan 2022 15:43:45 -0500 Subject: [PATCH 46/79] comment out ISPyB calls to database * if not done, the server startup is extremely slow * can be undone when ISPyB returns --- ispybLib.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/ispybLib.py b/ispybLib.py index 2fbcac78..4c78f2a5 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -19,13 +19,13 @@ #request_dicts = lsdb2.getColRequestsByTimeInterval('2018-02-14T00:00:00','2018-02-15T00:00:00') # Connect to ISPyB, get the relevant data area objects -conn = ispyb.open(conf_file) -core = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.CORE, conn) -mxacquisition = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXACQUISITION, conn) -mxprocessing = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXPROCESSING, conn) -mxscreening = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXSCREENING, conn) -cnx = conn.conn -cursor = cnx.cursor() +#conn = ispyb.open(conf_file) +#core = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.CORE, conn) +#mxacquisition = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXACQUISITION, conn) +#mxprocessing = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXPROCESSING, conn) +#mxscreening = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXSCREENING, conn) +#cnx = mysql.connector.connect(user='ispyb_api', password=os.environ['ISPYB_PASSWORD'],host='ispyb-db-dev.cs.nsls2.local',database='ispyb') +#cursor = cnx.cursor() beamline = os.environ["BEAMLINE_ID"] # Find the id for a particular @@ -56,6 +56,7 @@ def maxVisitNumfromProposal(propNum): def createPerson(firstName,lastName,loginName): + return params = core.get_person_params() params['given_name'] = firstName params['family_name'] = lastName @@ -65,6 +66,7 @@ def createPerson(firstName,lastName,loginName): def createProposal(propNum,PI_login="boaty"): + return pid = personIdFromLogin(PI_login) if (pid == 0): createPerson("Not","Sure",PI_login) @@ -79,6 +81,7 @@ def createProposal(propNum,PI_login="boaty"): cnx.commit() #not sure why I needed to do this. Maybe mistake in stored proc? def createVisitName(propNum): # this is for the GUI to know what a datapath would be in row_clicked + return logger.info("creating visit Name for propnum " + str(propNum)) propID = proposalIdFromProposal(propNum) if (propID == 0): #proposal doesn't exist, just create and assign to boaty @@ -94,6 +97,7 @@ def createVisitName(propNum): # this is for the GUI to know what a datapath woul def createVisit(propNum): + return visitName, newVisitNum = createVisitName(propNum) personID = personIdFromProposal(propNum) params = core.get_session_for_proposal_code_number_params() @@ -162,6 +166,7 @@ def createVisit(propNum): return visitName def addPersonToProposal(personLogin,propNum): + return personID = personIdFromLogin(personLogin) if (personID == 0): createPerson("Not","Sure",personLogin) @@ -179,6 +184,7 @@ def addPersonToProposal(personLogin,propNum): def insertPlotResult(dc_id,imageNumber,spotTotal,goodBraggCandidates,method2Res,totalIntegratedSignal): + return params = mxprocessing.get_quality_indicators_params() params['datacollectionid'] = dc_id params['imageNumber'] = imageNumber @@ -191,6 +197,7 @@ def insertPlotResult(dc_id,imageNumber,spotTotal,goodBraggCandidates,method2Res, def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None): #xmlfilename for fastDP #keep in mind that request type can be standard and result type be fastDP - multiple results per req. + return try: sessionid = core.retrieve_visit_id(visitName) @@ -329,6 +336,7 @@ def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None def createDataCollection(directory, filePrefix, jpegImageFilename, params, request_obj, sessionid): + return params['starttime'] = datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S') params['endtime'] = datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S') dcg_id = mxacquisition.insert_data_collection_group(list(params.values())) @@ -378,7 +386,8 @@ def createDataCollection(directory, filePrefix, jpegImageFilename, params, reque # if request_type == 'screening': # params['overlap'] = 89.0 -def insertRasterResult(request_id,visitName): +def insertRasterResult(request,visitName): + return try: sessionid = core.retrieve_visit_id(visitName) except ISPyBNoResultException as e: From 4516e1745b464c82f93dcc3f13fb2d9a8ea8366d Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Mon, 7 Feb 2022 10:28:59 -0500 Subject: [PATCH 47/79] fix for proposal name while ISPyB is down * temporarily just use mx-1 for all visits * disable newVisit() * do not call ispybLib * put into log and TODO messages that createVisit flag for setProposalID() and newVisit() won't work --- daq_utils.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/daq_utils.py b/daq_utils.py index 169b4f26..3fbf45ae 100644 --- a/daq_utils.py +++ b/daq_utils.py @@ -364,19 +364,26 @@ def readPVDesc(): counter_inf = line.split() counter_dict[counter_inf[1]] = beamline_designation + counter_inf[0] -def setProposalID(proposalID,createVisit=True): - if (getProposalID() != proposalID): #proposalID changed - create a new visit. - logger.info("you changed proposals! " + str(proposalID)) - try: - if (createVisit): - visitName = ispybLib.createVisit(proposalID) - db_lib.setBeamlineConfigParam(beamline,"proposal",proposalID) - else: - visitName, visitNum = ispybLib.createVisitName(proposalID) - except Exception as e: - visitName = "999999-1234" - logger.error("ispyb error in set proposal. Error: %s" % e) - setVisitName(visitName) +def createVisitNameRaw(proposalName, maxNumber=None): + if maxNumber: + number = maxNumber + 1 + else: + number = 1 + return f'mx{proposalName}-{number}', number + +def createVisitName(proposalName, maxNumber=None): + return createVisitNameRaw(proposalName, maxNumber) + +def setProposalID(proposalID,createVisit=True): # TODO JA proposalID implies a database ID, which it is not (just proposal number). Misleading + if (getProposalID() != proposalID): #proposalID changed - create a new visit. + logger.info("you changed proposals! " + str(proposalID)) + logger.info('ignoring createVisit for now - ISPyB required to properly account for visit numbers') + try: + visitName, visitNum = createVisitName(proposalID) + db_lib.setBeamlineConfigParam(beamline,"proposal",proposalID) + except Exception as e: + visitName = "999999-1234" + logger.error("error in set proposal. Error: %s" % e) def getProposalID(): return getBlConfig("proposal") From 7d1581a7b8d977f440735c8c9671471e318c296a Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 26 Sep 2023 13:43:03 -0400 Subject: [PATCH 48/79] restore setting visit name * lost during cherry-picking --- daq_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/daq_utils.py b/daq_utils.py index 3fbf45ae..6dca8948 100644 --- a/daq_utils.py +++ b/daq_utils.py @@ -384,6 +384,7 @@ def setProposalID(proposalID,createVisit=True): # TODO JA proposalID implies a except Exception as e: visitName = "999999-1234" logger.error("error in set proposal. Error: %s" % e) + setVisitName(visitName) def getProposalID(): return getBlConfig("proposal") From e58a4a313d224003efcbce3cf8c53760012de137 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Tue, 26 Sep 2023 15:33:27 -0400 Subject: [PATCH 49/79] calcLifetimeCB is only calculated if fmx --- gui/control_main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/control_main.py b/gui/control_main.py index 5bca9905..b9f70756 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -404,7 +404,8 @@ def createSampleTab(self): self.osc_end_ledit.textChanged[str].connect( functools.partial(self.totalExpChanged, "oscEnd") ) - self.osc_end_ledit.textChanged.connect(self.calcLifetimeCB) + if daq_utils.beamline == "fmx": + self.osc_end_ledit.textChanged.connect(self.calcLifetimeCB) hBoxColParams1.addWidget(colStartLabel) hBoxColParams1.addWidget(self.osc_start_ledit) hBoxColParams1.addWidget(self.colEndLabel) From 04cd2d8501ef822c0c3807105d70b5360c2a026a Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Thu, 28 Sep 2023 17:31:42 -0400 Subject: [PATCH 50/79] hard code ISPyB DC ID for fastDP processing * since ispybLib.insertResult() doesn't use this value, using 1 should be ok. * this needs to be reverted when ISPyB is re-enabled --- runFastDPH5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runFastDPH5.py b/runFastDPH5.py index 4263cd6f..66c845a0 100755 --- a/runFastDPH5.py +++ b/runFastDPH5.py @@ -31,7 +31,7 @@ node = sys.argv[5] runDimple = int(sys.argv[6]) dimpleNode = sys.argv[7] -ispybDCID = int(sys.argv[8]) +ispybDCID = 1 #int(sys.argv[8]) comm_s = f"ssh -q {node} \"{os.environ['MXPROCESSINGSCRIPTSDIR']}fast_dp.sh {request_id} {numstart}\"" logger.info(comm_s) From 8c58596fc0bf6c74ea399662f6d9730233730912 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Mon, 2 Oct 2023 09:40:03 -0400 Subject: [PATCH 51/79] use Pillow on server to resize crystal snapshot --- ispybLib.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ispybLib.py b/ispybLib.py index 4c78f2a5..796c9810 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -226,10 +226,13 @@ def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None daq_utils.take_crystal_picture(filename=jpegImagePrefix) jpegImageFilename = jpegImagePrefix+".jpg" jpegImageThumbFilename = jpegImagePrefix+"t.jpg" - node = db_lib.getBeamlineConfigParam(beamline,"adxvNode") - comm_s = f"ssh -q {node} \"{os.environ['MXPROCESSINGSCRIPTSDIR']}resize.sh {jpegImageFilename} {jpegImageThumbFilename} 40% \"&" logger.info('resizing image: %s' % comm_s) - os.system(comm_s) + resizeRatio = 0.4 + fullSnapshot = Image.open(jpegImageFilename) + resizeWidth = fullSnapshot.width * resizeRatio + resizeHeight = fullSnapshot.height * resizeRatio + thumbSnapshot = fullSnapshot.resize((int(resizeWidth), int(resizeHeight))) + thumbSnapshot.save(jpegImageThumbFilename) seqNum = int(det_lib.detector_get_seqnum()) node = db_lib.getBeamlineConfigParam(beamline,"adxvNode") From e0f74eea31204587dd4e83c93c247efde0c11157 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Mon, 2 Oct 2023 16:00:59 -0400 Subject: [PATCH 52/79] Added env variable healthcheck --- utils/healthcheck.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/utils/healthcheck.py b/utils/healthcheck.py index 7a123b66..0c7ff891 100644 --- a/utils/healthcheck.py +++ b/utils/healthcheck.py @@ -168,6 +168,14 @@ def check_curr_visit_dir() -> bool: return True +@healthcheck(name="check environment variables", remediation="", fatal=True) +def check_env_variables() -> bool: + env_vars = ["STAFF_GROUP", "NSLS2_API_URL"] + missing_vars = [var for var in env_vars if var not in os.environ] + if missing_vars: + check_env_variables.remidiation = f"Environment variable(s) not found: {','.join(missing_vars)}" + return False + return True @healthcheck(name="existence of environment file", remediation="", fatal=True) def check_env_file() -> bool: From ed862b90c01f69649d422d2236120fbb364c9a03 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Mon, 2 Oct 2023 16:14:20 -0400 Subject: [PATCH 53/79] Checks if user is proposal member or staff --- gui/dewar_tree.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py index d745e204..a8a4ab63 100644 --- a/gui/dewar_tree.py +++ b/gui/dewar_tree.py @@ -35,7 +35,7 @@ def __init__(self, parent: "ControlMain"): self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) self.setAnimated(True) self.model = QtGui.QStandardItemModel() - + # self.isExpanded = 1 self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.openMenu) @@ -141,7 +141,9 @@ def refreshTreeDewarView(self): self.collapseAll() self.scrollTo(self.currentIndex(), QtWidgets.QAbstractItemView.PositionAtCenter) - def add_samples_to_puck_tree(self, puckContents, parentItem: QtGui.QStandardItem, index_label): + def add_samples_to_puck_tree( + self, puckContents, parentItem: QtGui.QStandardItem, index_label + ): # Method will attempt to add samples to the puck. If you don't belong to the proposal, # it will not add samples and clear the puck information selectedIndex = None @@ -162,11 +164,9 @@ def add_samples_to_puck_tree(self, puckContents, parentItem: QtGui.QStandardItem sampleDict.setdefault(sample_id, db_lib.getSampleByID(sample_id)), ) - #if not IS_STAFF and not self.is_proposal_member(sample["proposalID"]): - if not self.is_proposal_member(sample["proposalID"]): - # If the user is not part of the proposal or is not staff, don't fill tree + if not IS_STAFF and not self.is_proposal_member(sample["proposalID"]): + # If the user is not part of the proposal and is not staff, don't fill tree # Clear the puck information and don't make it selectable - # parentItem.clearData() parentItem.setText(index_label) current_flags = parentItem.flags() parentItem.setFlags(current_flags & ~Qt.ItemFlag.ItemIsSelectable) # type: ignore @@ -227,20 +227,20 @@ def add_samples_to_puck_tree(self, puckContents, parentItem: QtGui.QStandardItem self.setCurrentIndex(current_index) self.parent.row_clicked(current_index) - def is_proposal_member(self, proposal) -> bool: + def is_proposal_member(self, proposal_id) -> bool: # Check if the user running LSDC is part of the sample's proposal - if proposal not in self.proposal_membership: - r = requests.get(f"{os.environ['NSLS_API_URL']}/proposal/{proposal}") + if proposal_id not in self.proposal_membership: + r = requests.get(f"{os.environ['NSLS2_API_URL']}/proposal/{proposal_id}") r.raise_for_status() response = r.json() - if "users" in response and getpass.getuser() in [ + if "users" in response and getpass.getuser() in [ user["username"] for user in response["users"] if "username" in user ]: - self.proposal_membership[proposal] = True + self.proposal_membership[proposal_id] = True else: logger.info(f"Users not found in response: {response}") - self.proposal_membership[proposal] = False - return self.proposal_membership[proposal] + self.proposal_membership[proposal_id] = False + return self.proposal_membership[proposal_id] def create_request_item(self, request) -> QtGui.QStandardItem: col_item = QtGui.QStandardItem( From 0c066321bd6c0ae1457f89f0bb3cd709fd659784 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Mon, 2 Oct 2023 16:19:47 -0400 Subject: [PATCH 54/79] Attempting to add proposal number to puck label --- gui/dewar_tree.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py index a8a4ab63..355dbc53 100644 --- a/gui/dewar_tree.py +++ b/gui/dewar_tree.py @@ -177,6 +177,8 @@ def add_samples_to_puck_tree( ) return + parentItem.setText(f"{index_label} pass-{sample['proposalID']}") + position_s = f'{j+1}-{sample.get("name", "")}' item = QtGui.QStandardItem( QtGui.QIcon(ICON), From 35717803e1c773201f7d2da8f5e3b7517446addc Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 3 Oct 2023 14:38:03 -0400 Subject: [PATCH 55/79] fix logging message - show both ratio and filename --- ispybLib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ispybLib.py b/ispybLib.py index 796c9810..19ed1e47 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -226,8 +226,8 @@ def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None daq_utils.take_crystal_picture(filename=jpegImagePrefix) jpegImageFilename = jpegImagePrefix+".jpg" jpegImageThumbFilename = jpegImagePrefix+"t.jpg" - logger.info('resizing image: %s' % comm_s) resizeRatio = 0.4 + logger.info(f'resizing image: ratio: {resizeRatio} filename: {jpegImageThumbFilename}') fullSnapshot = Image.open(jpegImageFilename) resizeWidth = fullSnapshot.width * resizeRatio resizeHeight = fullSnapshot.height * resizeRatio From d4827459d4370b173ce6f6aac7db6a91bd058393 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Tue, 3 Oct 2023 14:42:48 -0400 Subject: [PATCH 56/79] fix missing import --- ispybLib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ispybLib.py b/ispybLib.py index 19ed1e47..e1ec58c2 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -8,6 +8,7 @@ import db_lib import det_lib import time +from PIL import Image import logging logger = logging.getLogger(__name__) From 3d7c924c6bc51f232cdd9dcb9c5807824929f5bc Mon Sep 17 00:00:00 2001 From: Shekar V Date: Fri, 13 Oct 2023 02:00:04 -0400 Subject: [PATCH 57/79] Added single call to get all dewar data --- db_lib.py | 48 +++++++++++++++++++++++++++++++++++++++++------ gui/dewar_tree.py | 38 ++++++++++++++++++++----------------- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/db_lib.py b/db_lib.py index 85181422..a0321618 100755 --- a/db_lib.py +++ b/db_lib.py @@ -1,16 +1,15 @@ +import logging import os - import time - -import six - import uuid +from collections import defaultdict import amostra.client.commands as acc import conftrak.client.commands as ccc -from analysisstore.client.commands import AnalysisClient import conftrak.exceptions -import logging +import six +from analysisstore.client.commands import AnalysisClient + logger = logging.getLogger(__name__) #12/19 - Skinner inherited this from Hugo, who inherited it from Matt. Arman wrote the underlying DB and left BNL in 2018. @@ -486,6 +485,43 @@ def getContainerByID(container_id): c = getContainers(filters={'uid': container_id})[0] return c +def get_dewar_tree_data(dewar_name, beamline): + """ + returns all data required to show dewar tree data with minimum number of database accesses + """ + dewar_data = getContainers(filters={"name": dewar_name, "owner": beamline})[0] + + puck_ids = [ + pid for pid in dewar_data.get("content", []) if pid + ] # removes blank ids + pucks = getContainers(filters={"uid": {"$in": puck_ids}}) + + # Create a mega list of sample ids from puck information + sample_ids = [ + sample_id + for puck in pucks + for sample_id in puck.get("content", []) + if sample_id + ] + + # Get all sample info in one call + params = {"uid": {"$in": sample_ids}} + samples = sample_ref.find(as_document=False, **params) + + # Get all request info in one call + params = {"sample": {"$in": sample_ids}, "state": "active"} + reqs = list(request_ref.find(**params)) + + # Assemble data into dictionaries + puck_data = {puck["uid"]: puck for puck in pucks} + sample_data = {sample["uid"]: sample for sample in samples} + request_data = defaultdict(list) + for req in reqs: + request_data[req["sample"]].append(req) + + return dewar_data, puck_data, sample_data, request_data + + def getQueue(beamlineName): """ diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py index 355dbc53..02fbc03c 100644 --- a/gui/dewar_tree.py +++ b/gui/dewar_tree.py @@ -1,16 +1,19 @@ +import getpass import logging +import os import typing -import os, getpass + +import requests from qtpy import QtCore, QtGui, QtWidgets from qtpy.QtCore import Qt -import requests + import daq_utils import db_lib from config_params import ( DEWAR_SECTORS, + IS_STAFF, PUCKS_PER_DEWAR_SECTOR, SAMPLE_TIMER_DELAY, - IS_STAFF, ) if typing.TYPE_CHECKING: @@ -109,20 +112,17 @@ def set_mounted_sample(self, item): def refreshTreeDewarView(self): puck = "" self.model.clear() - dewarContents = db_lib.getContainerByName( + dewar_data, puck_data, sample_data, request_data = db_lib.get_dewar_tree_data( daq_utils.primaryDewarName, daq_utils.beamline - )["content"] + ) parentItem = self.model.invisibleRootItem() for i, puck_id in enumerate( - dewarContents + dewar_data["content"] ): # dewar contents is the list of puck IDs puck = "" puckName = "" if puck_id: - puck = containerDict.get( - puck_id, - containerDict.setdefault(puck_id, db_lib.getContainerByID(puck_id)), - ) + puck = puck_data[puck_id] puckName = puck["name"] sector, puck_pos = divmod(i, self.pucksPerDewarSector) index_s = f"{sector+1}{chr(puck_pos + ord('A'))}" @@ -132,7 +132,9 @@ def refreshTreeDewarView(self): parentItem.appendRow(item) if puck != "" and puckName != "private": puckContents = puck.get("content", []) - self.add_samples_to_puck_tree(puckContents, item, index_s) + self.add_samples_to_puck_tree( + puckContents, item, index_s, sample_data, request_data + ) self.setModel(self.model) self.model.itemChanged.connect(self.queueSelectedSample) if self.isExpanded: @@ -142,7 +144,12 @@ def refreshTreeDewarView(self): self.scrollTo(self.currentIndex(), QtWidgets.QAbstractItemView.PositionAtCenter) def add_samples_to_puck_tree( - self, puckContents, parentItem: QtGui.QStandardItem, index_label + self, + puckContents, + parentItem: QtGui.QStandardItem, + index_label, + sample_data, + request_data, ): # Method will attempt to add samples to the puck. If you don't belong to the proposal, # it will not add samples and clear the puck information @@ -159,10 +166,7 @@ def add_samples_to_puck_tree( parentItem.appendRow(item) continue - sample = sampleDict.get( - sample_id, - sampleDict.setdefault(sample_id, db_lib.getSampleByID(sample_id)), - ) + sample = sample_data[sample_id] if not IS_STAFF and not self.is_proposal_member(sample["proposalID"]): # If the user is not part of the proposal and is not staff, don't fill tree @@ -196,7 +200,7 @@ def add_samples_to_puck_tree( if sample_id == self.parent.selectedSampleID: logger.info("found " + str(self.parent.SelectedItemData)) selectedSampleIndex = self.model.indexFromItem(item) - sampleRequestList = db_lib.getRequestsBySampleID(sample_id) + sampleRequestList = request_data[sample_id] for request in sampleRequestList: if not ("protocol" in request["request_obj"]): continue From 5a17f0c7083aacfce9c8c17da6a04f6ac8a66a02 Mon Sep 17 00:00:00 2001 From: Jun Aishima Date: Mon, 16 Oct 2023 16:54:05 -0400 Subject: [PATCH 58/79] fix typo --- ispybLib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ispybLib.py b/ispybLib.py index 62c5a4ea..bfd8d42a 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -204,7 +204,7 @@ def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None if (resultType == 'fastDP'): mx_data_reduction_dict = xml_file_to_dict(xmlFileName) comm = mx_data_reduction_dict['AutoProcProgramContainer']['AutoProcProgram']['processingCommandLine'] - mx_data_reduction_dict['AutoProcProgramContainer']['AutoProcProgram']['processingCommandLine'] = comm[(len(comm)-255:] + mx_data_reduction_dict['AutoProcProgramContainer']['AutoProcProgram']['processingCommandLine'] = comm[len(comm)-255:] (app_id, ap_id, scaling_id, integration_id) = mx_data_reduction_to_ispyb(mx_data_reduction_dict, dc_id, mxprocessing) mxprocessing.upsert_program_ex(program_id=app_id,status=1) From 5e23a43f87a07f6f7d04be9e8edd4f3943609c8d Mon Sep 17 00:00:00 2001 From: Shekar V Date: Wed, 18 Oct 2023 12:57:48 -0400 Subject: [PATCH 59/79] Fixed displaying puck name with proposal number --- gui/dewar_tree.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py index 02fbc03c..474e1d62 100644 --- a/gui/dewar_tree.py +++ b/gui/dewar_tree.py @@ -181,7 +181,8 @@ def add_samples_to_puck_tree( ) return - parentItem.setText(f"{index_label} pass-{sample['proposalID']}") + if not parentItem.text().endswith(f"pass-{sample['proposalID']}"): + parentItem.setText(f"{parentItem.text()} pass-{sample['proposalID']}") position_s = f'{j+1}-{sample.get("name", "")}' item = QtGui.QStandardItem( From 060b810c9b98247e4d33f016ca80fdfeb5adb858 Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Thu, 19 Oct 2023 15:48:48 -0400 Subject: [PATCH 60/79] Added formatting to proposal id --- gui/dewar_tree.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py index 474e1d62..186b1f03 100644 --- a/gui/dewar_tree.py +++ b/gui/dewar_tree.py @@ -181,8 +181,9 @@ def add_samples_to_puck_tree( ) return - if not parentItem.text().endswith(f"pass-{sample['proposalID']}"): - parentItem.setText(f"{parentItem.text()} pass-{sample['proposalID']}") + proposal_id_text = f"(pass-{sample['proposalID']})" + if not parentItem.text().endswith(proposal_id_text): + parentItem.setText(f"{parentItem.text()} -- {proposal_id_text}") position_s = f'{j+1}-{sample.get("name", "")}' item = QtGui.QStandardItem( From 5b486bee57249554ce134279e9d3ef90e591e683 Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra Date: Thu, 2 Nov 2023 11:33:45 -0400 Subject: [PATCH 61/79] adding to dewar dialog --- config_params.py | 34 +--------------------------------- gui/dialog/dewar.py | 2 +- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/config_params.py b/config_params.py index 3d457b9b..3d278733 100644 --- a/config_params.py +++ b/config_params.py @@ -82,45 +82,13 @@ class RasterStatus(Enum): GOVERNOR_TIMEOUT = 120 # seconds for a governor move -DEWAR_SECTORS = {"amx": 8, "fmx": 8, "nyx": 5} +DEWAR_SECTORS = {"amx": 8, "fmx": 8, "nyx": 8} PUCKS_PER_DEWAR_SECTOR = {"amx": 3, "fmx": 3, "nyx": 3} cryostreamTempPV = {"amx": "AMX:cs700:gasT-I", "fmx": "FMX:cs700:gasT-I"} -<<<<<<< HEAD -VALID_EXP_TIMES = { - "amx": {"min": 0.005, "max": 1, "digits": 3}, - "fmx": {"min": 0.01, "max": 10, "digits": 3}, - "nyx": {"min": 0.002, "max": 10, "digits": 4}, -} -VALID_DET_DIST = { - "amx": {"min": 100, "max": 500, "digits": 3}, - "fmx": {"min": 137, "max": 2000, "digits": 2}, - "nyx": {"min": 100, "max": 500, "digits": 3}, -} -VALID_TOTAL_EXP_TIMES = { - "amx": {"min": 0.005, "max": 300, "digits": 3}, - "fmx": {"min": 0.01, "max": 300, "digits": 3}, - "nyx": {"min": 0.01, "max": 1000, "digits": 3}, -} -VALID_PREFIX_LENGTH = 25 # TODO centralize with spreadsheet validation? -VALID_PREFIX_NAME = "[0-9a-zA-Z-_]{0,%s}" % VALID_PREFIX_LENGTH -VALID_TRANSMISSION = { - "amx": {"min": 0.001, "max": 1.0, "digits": 3}, - "fmx": {"min": 0.001, "max": 1.0, "digits": 3}, - "nyx": {"min": 0.001, "max": 0.999, "digits": 3}, -} - -LSDC_SERVICE_USERS = ("lsdc-amx", "lsdc-fmx", "lsdc-nyx") -IS_STAFF = ( - True - if os.environ["STAFF_GROUP"] in [grp.getgrgid(g).gr_name for g in os.getgroups()] - else False -) -======= VALID_EXP_TIMES = {'amx':{'min':0.005, 'max':1, 'digits':3}, 'fmx':{'min':0.01, 'max':10, 'digits':3}, 'nyx':{'min':0.002, 'max':10, 'digits':4}} VALID_DET_DIST = {'amx':{'min': 100, 'max':500, 'digits':3}, 'fmx':{'min':137, 'max':2000, 'digits':2}, 'nyx':{'min':100, 'max':500, 'digits':3}} VALID_TOTAL_EXP_TIMES = {'amx':{'min':0.005, 'max':300, 'digits':3}, 'fmx':{'min':0.01, 'max':300, 'digits':3}, 'nyx':{'min':0.01, 'max':1000, 'digits':3}} VALID_PREFIX_LENGTH = 25 #TODO centralize with spreadsheet validation? VALID_PREFIX_NAME = '[0-9a-zA-Z-_]{0,%s}' % VALID_PREFIX_LENGTH ->>>>>>> new_md2 diff --git a/gui/dialog/dewar.py b/gui/dialog/dewar.py index b3ae55cd..016cb4a5 100644 --- a/gui/dialog/dewar.py +++ b/gui/dialog/dewar.py @@ -59,7 +59,7 @@ def initUI(self): for j in range(0, self.pucksPerDewarSector): dataIndex = (i * self.pucksPerDewarSector) + j self.allButtonList[dataIndex] = QtWidgets.QPushButton( - (str(self.data[dataIndex])) + '{}: {}'.format(str(dataIndex+1),str(self.data[dataIndex])) ) self.allButtonList[dataIndex].clicked.connect( functools.partial(self.on_button, str(dataIndex)) From ff179265d33f8543b7f3048107955560336d0901 Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra Date: Thu, 2 Nov 2023 11:45:54 -0400 Subject: [PATCH 62/79] changin dewar dialog --- config_params.py | 2 +- gui/dialog/dewar.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config_params.py b/config_params.py index 49198de9..0b5e87c9 100644 --- a/config_params.py +++ b/config_params.py @@ -76,7 +76,7 @@ class RasterStatus(Enum): GOVERNOR_TIMEOUT = 120 # seconds for a governor move -DEWAR_SECTORS = {'amx':8, 'fmx':8, 'nyx':5} +DEWAR_SECTORS = {'amx':8, 'fmx':8, 'nyx':8} PUCKS_PER_DEWAR_SECTOR = {'amx':3, 'fmx':3, 'nyx':3} cryostreamTempPV = {'amx': 'AMX:cs700:gasT-I', 'fmx': 'FMX:cs700:gasT-I'} diff --git a/gui/dialog/dewar.py b/gui/dialog/dewar.py index b3ae55cd..016cb4a5 100644 --- a/gui/dialog/dewar.py +++ b/gui/dialog/dewar.py @@ -59,7 +59,7 @@ def initUI(self): for j in range(0, self.pucksPerDewarSector): dataIndex = (i * self.pucksPerDewarSector) + j self.allButtonList[dataIndex] = QtWidgets.QPushButton( - (str(self.data[dataIndex])) + '{}: {}'.format(str(dataIndex+1),str(self.data[dataIndex])) ) self.allButtonList[dataIndex].clicked.connect( functools.partial(self.on_button, str(dataIndex)) From 05b85be92a279e850a94baebe61f4f2499d2fca4 Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra Date: Mon, 6 Nov 2023 21:23:36 -0500 Subject: [PATCH 63/79] changing control_main --- gui/control_main.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 3cc97ccb..53dc04ff 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -411,19 +411,10 @@ def createSampleTab(self): self.osc_end_ledit.textChanged[str].connect( functools.partial(self.totalExpChanged, "oscEnd") ) -<<<<<<< HEAD - if daq_utils.beamline == "fmx": - self.osc_end_ledit.textChanged.connect(self.calcLifetimeCB) - hBoxColParams1.addWidget(colStartLabel) - hBoxColParams1.addWidget(self.osc_start_ledit) - hBoxColParams1.addWidget(self.colEndLabel) - hBoxColParams1.addWidget(self.osc_end_ledit) -======= #hBoxColParams1.addWidget(colStartLabel) #hBoxColParams1.addWidget(self.osc_start_ledit) #hBoxColParams1.addWidget(self.colEndLabel) #hBoxColParams1.addWidget(self.osc_end_ledit) ->>>>>>> new_md2 hBoxColParams2 = QtWidgets.QHBoxLayout() colRangeLabel = QtWidgets.QLabel("Oscillation Width:") colRangeLabel.setFixedWidth(140) From 20ee8c06a022553f9396db0bcd77ec0be5f49702 Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra <133771854+NYSBC-Rudra@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:59:31 -0500 Subject: [PATCH 64/79] Fixing branch --- albulaUtils.py | 2 +- config_params.py | 54 +- daq_main2.py | 2 +- devices.py | 310 - docs/source/developer_notes/release_notes.rst | 33 - embl_robot.py | 7 +- gui/control_main.py | 5431 ----------------- gui/data_loc_info.py | 113 - gui/dewar_tree.py | 493 -- gui/dialog/__init__.py | 7 - gui/dialog/dewar.py | 92 - gui/dialog/puck_dialog.py | 93 - gui/dialog/raster_explore.py | 53 - gui/dialog/screen_defaults.py | 274 - gui/dialog/snap_comment.py | 40 - gui/dialog/staff_screen.py | 365 -- gui/dialog/user_screen.py | 256 - gui/raster.py | 98 - ispybLib.py | 18 +- md2_flyers.py | 269 - raddoseLib.py | 323 +- robot_lib.py | 10 +- setenergy_lsdc.py | 1239 ---- top_view.py | 6 +- utils/healthcheck.py | 8 - 25 files changed, 187 insertions(+), 9409 deletions(-) delete mode 100644 devices.py delete mode 100644 gui/control_main.py delete mode 100644 gui/data_loc_info.py delete mode 100644 gui/dewar_tree.py delete mode 100644 gui/dialog/__init__.py delete mode 100644 gui/dialog/dewar.py delete mode 100644 gui/dialog/puck_dialog.py delete mode 100644 gui/dialog/raster_explore.py delete mode 100644 gui/dialog/screen_defaults.py delete mode 100644 gui/dialog/snap_comment.py delete mode 100644 gui/dialog/staff_screen.py delete mode 100644 gui/dialog/user_screen.py delete mode 100644 gui/raster.py delete mode 100644 md2_flyers.py delete mode 100644 setenergy_lsdc.py diff --git a/albulaUtils.py b/albulaUtils.py index 134548e5..fb146e89 100644 --- a/albulaUtils.py +++ b/albulaUtils.py @@ -91,7 +91,7 @@ def _albulaDispFile(filename): logger.info('reading file: %s' % filename[0]) albulaSubFrame.loadFile(filename[0]) currentMasterH5 = filename[0] - sleep(0.5) # Sleep to allow Albula to load file. Otherwise the following goTo() is ignored + sleep(0.3) # Sleep to allow Albula to load file. Otherwise the following goTo() is ignored logger.debug('reading image number %s' % filename[1]) albulaSubFrame.goTo(filename[1]) except dectris.albula.DNoObject: diff --git a/config_params.py b/config_params.py index 7d5136ff..49198de9 100644 --- a/config_params.py +++ b/config_params.py @@ -1,42 +1,38 @@ from enum import Enum -import os, grp # BlConfig parameter variable names # rastering parameters -RASTER_TUNE_LOW_RES = "rasterTuneLowRes" -RASTER_TUNE_HIGH_RES = "rasterTuneHighRes" -RASTER_TUNE_ICE_RING_FLAG = "rasterTuneIceRingFlag" -RASTER_TUNE_RESO_FLAG = "rasterTuneResoFlag" -RASTER_TUNE_ICE_RING_WIDTH = "rasterTuneIceRingWidth" -RASTER_DOZOR_SPOT_LEVEL = "rasterDozorSpotLevel" -RASTER_NUM_CELLS_DELAY_THRESHOLD = "rasterNumCellsThresholdDelay" +RASTER_TUNE_LOW_RES = 'rasterTuneLowRes' +RASTER_TUNE_HIGH_RES = 'rasterTuneHighRes' +RASTER_TUNE_ICE_RING_FLAG = 'rasterTuneIceRingFlag' +RASTER_TUNE_RESO_FLAG = 'rasterTuneResoFlag' +RASTER_TUNE_ICE_RING_WIDTH = 'rasterTuneIceRingWidth' +RASTER_DOZOR_SPOT_LEVEL = 'rasterDozorSpotLevel' +RASTER_NUM_CELLS_DELAY_THRESHOLD = 'rasterNumCellsThresholdDelay' # timing delays -ISPYB_RESULT_ENTRY_DELAY = "ispybResultEntryDelay" -RASTER_LONG_SNAPSHOT_DELAY = "rasterLongSnapshotDelay" -RASTER_SHORT_SNAPSHOT_DELAY = "rasterShortSnapshotDelay" -RASTER_POST_SNAPSHOT_DELAY = "rasterPostSnapshotDelay" -RASTER_GUI_XREC_FILL_DELAY = "rasterGuiXrecFillDelay" +ISPYB_RESULT_ENTRY_DELAY = 'ispybResultEntryDelay' +RASTER_LONG_SNAPSHOT_DELAY = 'rasterLongSnapshotDelay' +RASTER_SHORT_SNAPSHOT_DELAY = 'rasterShortSnapshotDelay' +RASTER_POST_SNAPSHOT_DELAY = 'rasterPostSnapshotDelay' +RASTER_GUI_XREC_FILL_DELAY = 'rasterGuiXrecFillDelay' # governor transition gain/exposure times -LOW_MAG_GAIN_DA = "lowMagGainDA" -LOW_MAG_GAIN = "lowMagGain" -LOW_MAG_EXP_TIME_DA = "lowMagExptimeDA" -LOW_MAG_EXP_TIME = "lowMagExptime" +LOW_MAG_GAIN_DA = 'lowMagGainDA' +LOW_MAG_GAIN = 'lowMagGain' +LOW_MAG_EXP_TIME_DA = 'lowMagExptimeDA' +LOW_MAG_EXP_TIME = 'lowMagExptime' # top view -TOP_VIEW_CHECK = "topViewCheck" +TOP_VIEW_CHECK = 'topViewCheck' -CRYOSTREAM_ONLINE = ( - "cryostream_online" # consistent naming with hardware, such as robot_online -) +CRYOSTREAM_ONLINE = 'cryostream_online' # consistent naming with hardware, such as robot_online # constant values below # GUI default configuration -BEAM_CHECK = "beamCheck" -UNMOUNT_COLD_CHECK = "unmountColdCheck" - +BEAM_CHECK = 'beamCheck' +UNMOUNT_COLD_CHECK = 'unmountColdCheck' # raster request status updates class RasterStatus(Enum): @@ -45,14 +41,12 @@ class RasterStatus(Enum): database. The GUI can process raster data, e.g. filling heat maps, in parallel to the server moving motors and adjusting low mag cam. """ - NEW = 0 DRAWN = 1 READY_FOR_FILL = 2 READY_FOR_SNAPSHOT = 3 READY_FOR_REPROCESS = 4 - HUTCH_TIMER_DELAY = 500 SAMPLE_TIMER_DELAY = 100 SERVER_CHECK_DELAY = 2000 @@ -82,12 +76,10 @@ class RasterStatus(Enum): GOVERNOR_TIMEOUT = 120 # seconds for a governor move +DEWAR_SECTORS = {'amx':8, 'fmx':8, 'nyx':5} +PUCKS_PER_DEWAR_SECTOR = {'amx':3, 'fmx':3, 'nyx':3} -DEWAR_SECTORS = {"amx": 8, "fmx": 8, "nyx": 8} -PUCKS_PER_DEWAR_SECTOR = {"amx": 3, "fmx": 3, "nyx": 3} - - -cryostreamTempPV = {"amx": "AMX:cs700:gasT-I", "fmx": "FMX:cs700:gasT-I"} +cryostreamTempPV = {'amx': 'AMX:cs700:gasT-I', 'fmx': 'FMX:cs700:gasT-I'} VALID_EXP_TIMES = {'amx':{'min':0.005, 'max':1, 'digits':3}, 'fmx':{'min':0.01, 'max':10, 'digits':3}, 'nyx':{'min':0.002, 'max':10, 'digits':4}} VALID_DET_DIST = {'amx':{'min': 100, 'max':500, 'digits':3}, 'fmx':{'min':137, 'max':2000, 'digits':2}, 'nyx':{'min':100, 'max':500, 'digits':3}} diff --git a/daq_main2.py b/daq_main2.py index 6cf2da58..1e9ba043 100755 --- a/daq_main2.py +++ b/daq_main2.py @@ -12,7 +12,6 @@ from robot_lib import * from beamline_lib import * from gov_lib import setGovRobot -import getpass from start_bs import robot from embl_robot import EMBLRobot if isinstance(robot, EMBLRobot): @@ -21,6 +20,7 @@ else: print("not importing RobotControlLib") + import logging from logging import handlers logger = logging.getLogger() diff --git a/devices.py b/devices.py deleted file mode 100644 index 32ad69c1..00000000 --- a/devices.py +++ /dev/null @@ -1,310 +0,0 @@ -from ophyd import Device, Component as Cpt, EpicsSignal, EpicsSignalRO, PVPositioner -import socket -from ophyd.status import SubscriptionStatus -import os - -class MD2Positioner(PVPositioner): - setpoint = Cpt(EpicsSignal, 'Position', name='setpoint') - readback = Cpt(EpicsSignal, 'Position', name='readback') - state = Cpt(EpicsSignalRO, 'State', name='state') - done = Cpt(EpicsSignalRO, 'State', name='done') - precision = Cpt(EpicsSignalRO, 'Precision', name='precision') - done_value = 4 # MD2 Enum, 4 = Ready - # TODO: Add limits, settle_time, timeout or defaults for each - - def val(self): - return self.get().readback - -class ExporterComponent(Cpt): - def __init__(self, address, port, name, **kwargs): - super().__init__(self, **kwargs) - self.name = name - self.address = address - self.port = port - self.sock = None # socket.socket(socket.AF_INET, socket.SOCK_STREAM) - #self.sock.settimeout(5) - - def get(self): - return () - - def connect(self): - # for establishing connection, using context manager is preferred - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((self.address, self.port)) - - def disconnect(self): - self.sock.close() - self.sock = None - - def send_data(self, data): - STX = chr(2) - ETX = chr(3) - data = STX + data + ETX - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.connect((self.address, self.port)) - s.sendto(data.encode(), (self.address, self.port)) - state = s.recv(4096) - print(f'state: {self.decipher_reply(state)}') - ret = None - while ret == None: - output = s.recv(4096) - output = self.decipher_reply(output) - ret = self.process_ret(output) - return ret - - def read_stream(self): - output = None - while output == None: - output, addr = self.sock.recvfrom(4096) - print(f'output:{self.decipher_reply(output)}') - output = None - - def write(self, attribute, value): - return self.send_data('WRTE ' + attribute + ' ' + str(value)) - - def read(self, attribute): - return (self.send_data('READ ' + attribute + ' ')).split(" ")[0][4:] - - def cmd(self, method, parameters): - parameters = map(str, parameters) - params = "\t".join(parameters) - return self.send_data('EXEC ' + method + ' ' + str(params)) - - def decipher_reply(self, reply): - # Specifically for decoding MD2 Exporter replies - reply = str(reply) - reply = reply.replace("\\x03", "") - reply = reply.replace("\\x1f", ", ") - reply = reply.replace("\\x02", "\n") - reply = reply.replace("\\t", " ") - return reply[2:-1] - - def process_ret(self, ret): - # Returns only when an error or return value is found, - # ignores events to ensure flyer kickoff functions as expected - for line in ret.split("\n"): - if "ERR:" in line: - print(f"error: {line}") - return line - elif "RET:" in line: - print(f"returned: {line}") - return line - elif "NULL" in line: - print(f"null error: {line}") - return line - elif "EVT:" in line: # print here if you want to see the events - pass #return self.process_evt(line) - -class LightDevice(Device): - control = Cpt(EpicsSignal, 'LightIsOn', name='control') - factor = Cpt(EpicsSignal, 'LightFactor', name='factor') - level = Cpt(EpicsSignal, 'LightLevel', name='level') - - def is_on(self): - return self.control.get() == 1 - - def turn_on(self): - self.control.set(1) - - def turn_off(self): - self.control.set(0) - - def set_factor(self, factor): - self.factor.set(factor) - - def set_level(self, level): - self.level.set(level) - -class BeamstopDevice(Device): - distance = Cpt(MD2Positioner, "BeamstopDistance", name="distance") - x = Cpt(MD2Positioner, "BeamstopX", name="x") - y = Cpt(MD2Positioner, "BeamstopY", name="y") - z = Cpt(MD2Positioner, "BeamstopZ", name="z") - pos = Cpt(EpicsSignal, "BeamstopPosition", name="pos") - -class MD2SimpleHVDevice(Device): - horizontal = Cpt(MD2Positioner, "HVHorizontal", name="horizontal") - vertical = Cpt(MD2Positioner, "HVVertical", name="vertical") - pos = Cpt(EpicsSignal, "HVPosition", name="pos") - # Current aperture/scintillator/capillary predifined position. - # Enum: the aperture position: - # 0: PARK, under cover. - # 1: BEAM, selected aperture aligned with beam. - # 2: OFF, just below the OAV. - # 3: UNKNOWN, not in a predefined position (this cannot be set). - -class GonioDevice(Device): - omega = Cpt(MD2Positioner, 'Omega',name='omega') - x = Cpt(MD2Positioner, 'AlignmentX',name='x') - y = Cpt(MD2Positioner, 'AlignmentY',name='y') - z = Cpt(MD2Positioner, 'AlignmentZ',name='z') - cx = Cpt(MD2Positioner, 'CentringX',name='cx') - cy = Cpt(MD2Positioner, 'CentringY',name='cy') - -class MD2Device(GonioDevice): - # TODO: Enum for MD2 phases and states - cx = Cpt(MD2Positioner, 'CentringX',name='cx') - cy = Cpt(MD2Positioner, 'CentringY',name='cy') - center_pixel_x = Cpt(EpicsSignalRO, 'BeamPositionHorizontal',name='center_pixel_x') - center_pixel_y = Cpt(EpicsSignalRO, 'BeamPositionVertical',name='center_pixel_y') - centring_click = Cpt(EpicsSignal, 'setCentringClick',name='centring_click') - state = Cpt(EpicsSignalRO, 'State',name='state') - phase = Cpt(EpicsSignal, 'CurrentPhase',name='phase') - phase_index = Cpt(EpicsSignalRO, 'CurrentPhaseIndex',name='phase_index') - detector_state = Cpt(EpicsSignal, 'DetectorState',name='det_state') - detector_gate_pulse_enabled = Cpt(EpicsSignal, 'DetectorGatePulseEnabled',name='det_gate_pulse_enabled') - exporter = Cpt(ExporterComponent, address=os.environ['EXPORTER_HOST'], port=int(os.environ['EXPORTER_PORT']), name='exporter') - - def is_ready(self): - return self.state.get() == 4 - - def ready_status(self): - # returns an ophyd status object that monitors the state pv for operations to complete - def check_ready(*, old_value, value, **kwargs): - "Return True when the MD2 is ready" - return (value == 4) - return SubscriptionStatus(self.state, check_ready) - - def phase_transition(self, phase): - # returns an ophyd status object that monitors the phase pv for operations to complete - self.phase.put(phase) - def check_transition_state(*, old_value, value, **kwargs): - "Return True when the MD2 is ready" - return (value == 4 and self.phase.get() == phase) - return SubscriptionStatus(self.state, check_transition_state) - - def standard_scan(self, - frame_number=0, # int: frame ID just for logging purposes. - num_images=1, # int: number of frames. Needed solely when the detector use gate enabled trigger. - start_angle=0, # double: angle (deg) at which the shutter opens and omega speed is stable. - scan_range=0.1, # double: omega relative move angle (deg) before closing the shutter. - exposure_time=0.1, # double: exposure time (sec) to control shutter command. - num_passes=1 # int: number of moves forward and reverse between start angle and stop angle - ): - command = 'startScanEx2' - if start_angle is None: - start_angle=self.omega.get() - return self.exporter.cmd(command, [frame_number, num_images, start_angle, scan_range, exposure_time, num_passes]) - - def vector_scan(self, - start_angle=None, # double: angle (deg) at which the shutter opens and omega speed is stable. - scan_range=10, # double: omega relative move angle (deg) before closing the shutter. - exposure_time=1, # double: exposure time (sec) to control shutter command. - start_y=None, # double: PhiY axis position at the beginning of the exposure. - start_z=None, # double: PhiZ axis position at the beginning of the exposure. - start_cx=None, # double: CentringX axis position at the beginning of the exposure. - start_cy=None, # double: CentringY axis position at the beginning of the exposure. - stop_y=None, # double: PhiY axis position at the end of the exposure. - stop_z=None, # double: PhiZ axis position at the end of the exposure. - stop_cx=None, # double: CentringX axis position at the end of the exposure. - stop_cy=None, # double: CentringY axis position at the end of the exposure. - ): - command = 'startScan4DEx' - if start_angle is None: - start_angle = self.omega.val() - if start_y is None: - start_y = self.y.val() - if start_z is None: - start_z = self.z.val() - if start_cx is None: - start_cx = self.cx.val() - if start_cy is None: - start_cy = self.cy.val() - if stop_y is None: - stop_y = self.y.val() - if stop_z is None: - stop_z = self.z.val() - if stop_cx is None: - stop_cx = self.cx.val() - if stop_cy is None: - stop_cy = self.cy.val() - - # List of scan parameters values, comma separated. The axes start values define the beginning - # of the exposure, that is when all the axes have a steady speed and when the shutter/detector - # are triggered. - # The axes stop values are for the end of detector exposure and define the position at the - # beginning of the deceleration. - # Inputs names: "start_angle", "scan_range", "exposure_time", "start_y", "start_z", "start_cx", - # "start_cy", "stop_y", "stop_z", "stop_cx", "stop_cy" - param_list = [start_angle, scan_range, exposure_time, - start_y, start_z, start_cx, start_cy, - stop_y, stop_z, stop_cx, stop_cy] - return self.exporter.cmd(command, param_list) - - def raster_scan(self, - omega_range=0, # double: omega relative move angle (deg) before closing the shutter. - line_range=0.1, # double: horizontal range of the grid (mm). - total_uturn_range=0.1, # double: vertical range of the grid (mm). - start_omega=None, # double: angle (deg) at which the shutter opens and omega speed is stable. - start_y=None, # double: PhiY axis position at the beginning of the exposure. - start_z=None, # double: PhiZ axis position at the beginning of the exposure. - start_cx=None, # double: CentringX axis position at the beginning of the exposure. - start_cy=None, # double: CentringY axis position at the beginning of the exposure. - number_of_lines=5, # int: number of frames on the vertical range. - frames_per_line=5, # int: number of frames on the horizontal range. - exposure_time=1.2, # double: exposure time (sec) to control shutter command. +1, based on the exaples given - invert_direction=True, # boolean: true to enable passes in the reverse direction. - use_centring_table=True, # boolean: true to use the centring table to do the pitch movements. - use_fast_mesh_scans=True # boolean: true to use the fast raster scan if available (power PMAC). - ): - - command = 'startRasterScanEx' - if start_omega is None: - start_omega = self.omega.val() - if start_y is None: - start_y = self.y.val() - if start_z is None: - start_z = self.z.val() - if start_cx is None: - start_cx = self.cx.val() - if start_cy is None: - start_cy = self.cy.val() - # List of scan parameters values, "/t" separated. The axes start values define the beginning - # of the exposure, that is when all the axes have a steady speed and when the shutter/detector - # are triggered. - # The axes stop values are for the end of detector exposure and define the position at the - # beginning of the deceleration. - # Inputs names: "omega_range", "line_range", "total_uturn_range", "start_omega", "start_y", - # "start_z", "start_cx", "start_cy", "number_of_lines", "frames_per_lines", "exposure_time", - # "invert_direction", "use_centring_table", "use_fast_mesh_scans" - param_list = [omega_range, line_range, total_uturn_range, start_omega, start_y, start_z, - start_cx, start_cy, number_of_lines, frames_per_line, exposure_time, - invert_direction, use_centring_table, use_fast_mesh_scans] - return self.exporter.cmd(command, param_list) - -class MD2ApertureDevice(Device): - # this device needs additional signals for "CurrentApertureDiameterIndex" and "ApertureDiameters" - current_index = Cpt(EpicsSignal, 'CurrentApertureDiameterIndex', name='current_index') - diameters = Cpt(EpicsSignalRO, 'ApertureDiameters', name='diameters') - - def get_diameter_list(self): - # must format list for GUI - # also, can't currently get from PV - return self.diameters.get() - - def set_diameter(self, diameter): - if diameter in self.get_diameter_list(): - self.current_index.put(self.get_diameter_list().index(diameter)) - -class ShutterDevice(Device): - control = Cpt(EpicsSignal, '{MD2}:FastShutterIsOpen', name='control') # PV to send control signal - pos_opn = Cpt(EpicsSignalRO, '{Gon:1-Sht}Pos:Opn-I', name='pos_opn') - pos_cls = Cpt(EpicsSignalRO, '{Gon:1-Sht}Pos:Cls-I', name='pos_cls') - - def is_open(self): - return self.control.get() == 1 #self.pos_opn.get() - - def open_shutter(self): - self.control.set(1)#self.pos_opn.get()) iocs are down, so just setting it to 1 - - def close_shutter(self): - self.control.set(0)#self.pos_cls.get()) - -class CameraDevice(Device): - # MD2 camera has the following attributes: - # CoaxCamScaleX - # CoaxCamScaleY - # CoaxialCameraZoomValue - scale_x = Cpt(EpicsSignalRO, 'CoaxCamScaleX', name='scale_x') - scale_y = Cpt(EpicsSignalRO, 'CoaxCamScaleY', name='scale_y') - zoom = Cpt(EpicsSignal, 'CoaxialCameraZoomValue', name='zoom') diff --git a/docs/source/developer_notes/release_notes.rst b/docs/source/developer_notes/release_notes.rst index 9b3d18e1..54c0b40f 100644 --- a/docs/source/developer_notes/release_notes.rst +++ b/docs/source/developer_notes/release_notes.rst @@ -2,39 +2,6 @@ Release History ================= -2.0.2 (2023-09-12, a.k.a. 2023-3) -================================= - -Fixes and other changes ------------------------ - -* GUI - - * GUI code split up into its own module - * Fixes for lifetime calculation - * Improve Albula handling - -* Server and GUI security improvements - - * Use external file to determine visit directory - * Use same visit directory location for server and GUI, and force all files acquired by LSDC to be written to that directory - * Enforce GUI and server starting in the visit directory - * Ensure server is started as one of the known LSDC service users (as opposed to a staff member in n2sn-inststaff-) - - -* Improved synchronization of detector and governor -* Re-enable ISPyB storage of data collections and processing results -* Standardize handling of FMX towards AMX when mounting a sample -* Move storage of raster results in ISPyB onto server, to remove ISPyB dependence in GUI -* Save FMX flux reference after energy change - -* NYX-specific (2023-2-nyx) - not merged into master due to significant differences - - * GUI improvements - - * General layout - * NYX-specific changes - 2.0.1 (2023-04-20, a.k.a. 2023-2) ================================= diff --git a/embl_robot.py b/embl_robot.py index 622106ec..653f0daf 100644 --- a/embl_robot.py +++ b/embl_robot.py @@ -262,6 +262,9 @@ def mount(self, gov_robot, puckPos,pinPos,sampID,**kwargs): else: robotStatus = beamline_support.get_any_epics_pv("SW:RobotState","VAL") if (robotStatus != "Ready"): + if (daq_utils.beamline == "fmx"): + daq_macros.homePins() + time.sleep(3.0) gov_status = gov_lib.setGovRobot(gov_robot, 'SE') if not gov_status.success: return MOUNT_FAILURE @@ -307,7 +310,7 @@ def mount(self, gov_robot, puckPos,pinPos,sampID,**kwargs): else: if (retryMountCount == 0): retryMountCount+=1 - mountStat = self.mount(gov_robot, puckPos,pinPos,sampID, **kwargs) + mountStat = self.mount(puckPos,pinPos,sampID, kwargs) if (mountStat == MOUNT_STEP_SUCCESSFUL): retryMountCount = 0 return mountStat @@ -358,8 +361,6 @@ def preUnmount(self, gov_robot, puckPos,pinPos,sampID): #will somehow know where detDist = beamline_lib.motorPosFromDescriptor("detectorDist") if (detDist 1: - if sys.argv[1] == "master": - self.changeControlMasterCB(1) - self.controlMasterCheckBox.setChecked(True) - self.XRFInfoDict = self.parseXRFTable() # I don't like this - #self.dewarTree.refreshTreeDewarView() - - def setGuiValues(self, values): - for item, value in values.items(): - logger.info("resetting %s to %s" % (item, value)) - if item == "osc_start": - self.osc_start_ledit.setText("%.3f" % float(value)) - elif item == "osc_end": - self.osc_end_ledit.setText("%.3f" % float(value)) - elif item == "osc_range": - self.osc_range_ledit.setText("%.3f" % float(value)) - elif item == "img_width": - self.img_width_ledit.setText("%.3f" % float(value)) - elif item == "exp_time": - self.exp_time_ledit.setText("%.3f" % float(value)) - elif item == "transmission": - self.transmission_ledit.setText("%.3f" % float(value)) - elif item == "resolution": - self.resolution_ledit.setText("%.2f" % float(value)) - else: - logger.error("setGuiValues unknown item: %s value: %s" % (item, value)) - - def parseXRFTable(self): - XRFFile = open(os.environ["CONFIGDIR"] + "/XRF-AMX_simple.txt") - XRFInfoDict = {} - for line in XRFFile.readlines(): - tokens = line.split() - XRFInfoDict[tokens[0]] = int(float(tokens[5]) * 100) - XRFFile.close() - return XRFInfoDict - - def closeEvent(self, evnt): - evnt.accept() - sys.exit() # doing this to close any windows left open - - def initVideo2(self, frequency): - #self.captureHighMag = cv2.VideoCapture(daq_utils.highMagCamURL) - logger.debug('highMagCamURL: "' + daq_utils.highMagCamURL + '"') - - def initVideo4(self, frequency): - #self.captureHighMagZoom = cv2.VideoCapture(daq_utils.highMagZoomCamURL) - logger.debug('highMagZoomCamURL: "' + daq_utils.highMagZoomCamURL + '"') - - def initVideo3(self, frequency): - #self.captureLowMagZoom = cv2.VideoCapture(daq_utils.lowMagZoomCamURL) - logger.debug('lowMagZoomCamURL: "' + daq_utils.lowMagZoomCamURL + '"') - - def createSampleTab(self): - sampleTab = QtWidgets.QWidget() - splitter1 = QtWidgets.QSplitter(Qt.Horizontal) - vBoxlayout = QtWidgets.QVBoxLayout() - self.dewarTreeFrame = QFrame() - vBoxDFlayout = QtWidgets.QVBoxLayout() - self.selectedSampleRequest = {} - self.selectedSampleID = "" - self.dewarTree = DewarTree(self) - self.dewarTree.clicked[QModelIndex].connect(self.row_clicked) - treeSelectBehavior = QtWidgets.QAbstractItemView.SelectItems - treeSelectMode = QtWidgets.QAbstractItemView.ExtendedSelection - self.dewarTree.setSelectionMode(treeSelectMode) - self.dewarTree.setSelectionBehavior(treeSelectBehavior) - hBoxRadioLayout1 = QtWidgets.QHBoxLayout() - self.viewRadioGroup = QtWidgets.QButtonGroup() - self.priorityViewRadio = QtWidgets.QRadioButton("PriorityView") - self.priorityViewRadio.toggled.connect( - functools.partial(self.dewarViewToggledCB, "priorityView") - ) - self.viewRadioGroup.addButton(self.priorityViewRadio) - self.dewarViewRadio = QtWidgets.QRadioButton("DewarView") - self.dewarViewRadio.setChecked(True) - self.dewarViewRadio.toggled.connect( - functools.partial(self.dewarViewToggledCB, "dewarView") - ) - hBoxRadioLayout1.addWidget(self.dewarViewRadio) - hBoxRadioLayout1.addWidget(self.priorityViewRadio) - self.viewRadioGroup.addButton(self.dewarViewRadio) - vBoxDFlayout.addLayout(hBoxRadioLayout1) - vBoxDFlayout.addWidget(self.dewarTree) - queueSelectedButton = QtWidgets.QPushButton("Queue All Selected") - queueSelectedButton.clicked.connect(self.dewarTree.queueAllSelectedCB) - deQueueSelectedButton = QtWidgets.QPushButton("deQueue All Selected") - deQueueSelectedButton.clicked.connect(self.dewarTree.deQueueAllSelectedCB) - runQueueButton = QtWidgets.QPushButton("Collect Queue") - runQueueButton.setStyleSheet("background-color: green") - runQueueButton.clicked.connect(self.collectQueueCB) - stopRunButton = QtWidgets.QPushButton("Stop Collection") - stopRunButton.setStyleSheet("background-color: red") - stopRunButton.clicked.connect(self.stopRunCB) # immediate stop everything - puckToDewarButton = QtWidgets.QPushButton("Puck to Dewar...") - mountSampleButton = QtWidgets.QPushButton("Mount Sample") - mountSampleButton.clicked.connect(self.mountSampleCB) - unmountSampleButton = QtWidgets.QPushButton("Unmount Sample") - unmountSampleButton.clicked.connect(self.unmountSampleCB) - puckToDewarButton.clicked.connect(self.puckToDewarCB) - removePuckButton = QtWidgets.QPushButton("Remove Puck...") - removePuckButton.clicked.connect(self.removePuckCB) - expandAllButton = QtWidgets.QPushButton("Expand All") - expandAllButton.clicked.connect(self.dewarTree.expandAllCB) - collapseAllButton = QtWidgets.QPushButton("Collapse All") - collapseAllButton.clicked.connect(self.dewarTree.collapseAllCB) - self.pauseQueueButton = QtWidgets.QPushButton("Pause") - self.pauseQueueButton.clicked.connect(self.stopQueueCB) - emptyQueueButton = QtWidgets.QPushButton("Empty Queue") - emptyQueueButton.clicked.connect( - functools.partial(self.dewarTree.deleteSelectedCB, 1) - ) - warmupButton = QtWidgets.QPushButton("Warmup Gripper") - warmupButton.clicked.connect(self.warmupGripperCB) - restartServerButton = QtWidgets.QPushButton("Restart Server") - restartServerButton.clicked.connect(self.restartServerCB) - self.openShutterButton = QtWidgets.QPushButton("Open Photon Shutter") - self.openShutterButton.clicked.connect(self.openPhotonShutterCB) - self.popUserScreen = QtWidgets.QPushButton("User Screen...") - self.popUserScreen.clicked.connect(self.popUserScreenCB) - self.closeShutterButton = QtWidgets.QPushButton("Close Photon Shutter") - self.closeShutterButton.clicked.connect(self.closePhotonShutterCB) - self.closeShutterButton.setStyleSheet("background-color: red") - self.parkRobotButton = QtWidgets.QPushButton("Park Robot") - self.parkRobotButton.clicked.connect(self.parkRobotCB) - hBoxTreeButtsLayout = QtWidgets.QHBoxLayout() - vBoxTreeButtsLayoutLeft = QtWidgets.QVBoxLayout() - vBoxTreeButtsLayoutRight = QtWidgets.QVBoxLayout() - vBoxTreeButtsLayoutLeft.addWidget(runQueueButton) - vBoxTreeButtsLayoutLeft.addWidget(mountSampleButton) - vBoxTreeButtsLayoutLeft.addWidget(self.pauseQueueButton) - vBoxTreeButtsLayoutLeft.addWidget(queueSelectedButton) - vBoxTreeButtsLayoutLeft.addWidget(self.popUserScreen) - vBoxTreeButtsLayoutLeft.addWidget(warmupButton) - vBoxTreeButtsLayoutRight.addWidget(self.closeShutterButton) - vBoxTreeButtsLayoutRight.addWidget(self.parkRobotButton) - vBoxTreeButtsLayoutRight.addWidget(unmountSampleButton) - vBoxTreeButtsLayoutRight.addWidget(deQueueSelectedButton) - vBoxTreeButtsLayoutRight.addWidget(emptyQueueButton) - #vBoxTreeButtsLayoutRight.addWidget(restartServerButton) - hBoxTreeButtsLayout.addLayout(vBoxTreeButtsLayoutLeft) - hBoxTreeButtsLayout.addLayout(vBoxTreeButtsLayoutRight) - vBoxDFlayout.addLayout(hBoxTreeButtsLayout) - self.dewarTreeFrame.setLayout(vBoxDFlayout) - splitter1.addWidget(self.dewarTreeFrame) - splitter11 = QtWidgets.QSplitter(Qt.Horizontal) - self.mainSetupFrame = QFrame() - self.mainSetupFrame.setFixedHeight(890) - vBoxMainSetup = QtWidgets.QVBoxLayout() - self.mainToolBox = QtWidgets.QToolBox() - self.mainToolBox.setMinimumWidth(750) - self.mainColFrame = QFrame() - vBoxMainColLayout = QtWidgets.QVBoxLayout() - colParamsGB = QtWidgets.QGroupBox() - colParamsGB.setTitle("Acquisition") - vBoxColParams1 = QtWidgets.QVBoxLayout() - hBoxColParams1 = QtWidgets.QHBoxLayout() - colStartLabel = QtWidgets.QLabel("Oscillation Start:") - colStartLabel.setFixedWidth(140) - colStartLabel.setAlignment(QtCore.Qt.AlignCenter) - self.osc_start_ledit = QtWidgets.QLineEdit() - self.osc_start_ledit.setFixedWidth(60) - self.osc_start_ledit.setValidator(QtGui.QDoubleValidator()) - self.colEndLabel = QtWidgets.QLabel("Oscillation Range:") - self.colEndLabel.setAlignment(QtCore.Qt.AlignCenter) - self.colEndLabel.setFixedWidth(140) - self.osc_end_ledit = QtWidgets.QLineEdit() - self.setGuiValues({"osc_end": "180.0"}) - self.osc_end_ledit.setFixedWidth(60) - self.osc_end_ledit.setValidator(QtGui.QDoubleValidator()) - self.osc_end_ledit.textChanged[str].connect( - functools.partial(self.totalExpChanged, "oscEnd") - ) - #hBoxColParams1.addWidget(colStartLabel) - #hBoxColParams1.addWidget(self.osc_start_ledit) - #hBoxColParams1.addWidget(self.colEndLabel) - #hBoxColParams1.addWidget(self.osc_end_ledit) - hBoxColParams2 = QtWidgets.QHBoxLayout() - colRangeLabel = QtWidgets.QLabel("Oscillation Width:") - colRangeLabel.setFixedWidth(140) - colRangeLabel.setAlignment(QtCore.Qt.AlignCenter) - self.osc_range_ledit = QtWidgets.QLineEdit() - self.osc_range_ledit.setFixedWidth(60) - self.osc_range_ledit.setValidator(QtGui.QDoubleValidator(0.001, 3600, 3)) - self.stillModeCheckBox = QCheckBox("Stills") - self.stillModeCheckBox.setEnabled(False) - if self.stillModeStatePV.get(): - self.stillModeCheckBox.setChecked(True) - self.setGuiValues({"osc_range": "0.0"}) - self.osc_range_ledit.setEnabled(False) - else: - self.stillModeCheckBox.setChecked(False) - self.osc_range_ledit.setEnabled(True) - colExptimeLabel = QtWidgets.QLabel("ExposureTime:") - self.stillModeCheckBox.clicked.connect(self.stillModeUserPushCB) - self.osc_range_ledit.textChanged[str].connect( - functools.partial(self.totalExpChanged, "oscRange") - ) - colExptimeLabel.setFixedWidth(140) - colExptimeLabel.setAlignment(QtCore.Qt.AlignCenter) - self.exp_time_ledit = QtWidgets.QLineEdit() - self.exp_time_ledit.setFixedWidth(60) - self.exp_time_ledit.textChanged[str].connect(self.totalExpChanged) - self.exp_time_ledit.setValidator( - QtGui.QDoubleValidator( - VALID_EXP_TIMES[daq_utils.beamline]["min"], - VALID_EXP_TIMES[daq_utils.beamline]["max"], - VALID_EXP_TIMES[daq_utils.beamline]["digits"], - ) - ) - self.exp_time_ledit.textChanged.connect(self.checkEntryState) - #hBoxColParams2.addWidget(colRangeLabel) - #hBoxColParams2.addWidget(self.osc_range_ledit) - - #hBoxColParams2.addWidget(colExptimeLabel) - #hBoxColParams2.addWidget(self.exp_time_ledit) - hBoxColParams25 = QtWidgets.QHBoxLayout() - #hBoxColParams25.addWidget(self.stillModeCheckBox) - totalExptimeLabel = QtWidgets.QLabel("Total Exposure Time (s):") - totalExptimeLabel.setFixedWidth(155) - totalExptimeLabel.setAlignment(QtCore.Qt.AlignCenter) - self.totalExptime_ledit = QtWidgets.QLineEdit() - self.totalExptime_ledit.setReadOnly(True) - self.totalExptime_ledit.setFrame(False) - self.totalExptime_ledit.setFixedWidth(60) - self.totalExptime_ledit.setValidator( - QtGui.QDoubleValidator( - VALID_TOTAL_EXP_TIMES[daq_utils.beamline]["min"], - VALID_TOTAL_EXP_TIMES[daq_utils.beamline]["max"], - VALID_TOTAL_EXP_TIMES[daq_utils.beamline]["digits"], - ) - ) - self.totalExptime_ledit.textChanged.connect(self.checkEntryState) - - sampleLifetimeLabel = QtWidgets.QLabel("Estimated Sample Lifetime (s): ") - if daq_utils.beamline == "amx": - self.sampleLifetimeReadback = QtEpicsPVLabel( - daq_utils.pvLookupDict["sampleLifetime"], self, 70, 2 - ) - self.sampleLifetimeReadback_ledit = self.sampleLifetimeReadback.getEntry() - else: - calcLifetimeButton = QtWidgets.QPushButton("Calc. Lifetime") - calcLifetimeButton.clicked.connect(self.calcLifetimeCB) - self.sampleLifetimeReadback_ledit = QtWidgets.QLabel() - self.calcLifetimeCB() - #hBoxColParams25.addWidget(totalExptimeLabel) - #hBoxColParams25.addWidget(self.totalExptime_ledit) - # if (daq_utils.beamline == "fmx"): - # hBoxColParams25.addWidget(calcLifetimeButton) - #hBoxColParams25.addWidget(sampleLifetimeLabel) - #hBoxColParams25.addWidget(self.sampleLifetimeReadback_ledit) - hBoxColParams22 = QtWidgets.QHBoxLayout() - if daq_utils.beamline in ("fmx", "nyx"): - if getBlConfig("attenType") == "RI": - self.transmissionReadback = QtEpicsPVLabel( - daq_utils.pvLookupDict["RI_Atten_SP"], self, 60, 3 - ) - self.transmissionSetPoint = QtEpicsPVEntry( - daq_utils.pvLookupDict["RI_Atten_SP"], self, 60, 3 - ) - colTransmissionLabel = QtWidgets.QLabel("Transmission (RI) (0.0-1.0):") - else: - self.transmissionReadback = QtEpicsPVLabel( - daq_utils.pvLookupDict["transmissionRBV"], self, 60, 3 - ) - self.transmissionSetPoint = QtEpicsPVEntry( - daq_utils.pvLookupDict["transmissionSet"], self, 60, 3 - ) - colTransmissionLabel = QtWidgets.QLabel("Transmission (BCU) (0.0-1.0):") - else: - self.transmissionReadback = QtEpicsPVLabel( - daq_utils.pvLookupDict["transmissionRBV"], self, 60, 3 - ) - self.transmissionSetPoint = QtEpicsPVEntry( - daq_utils.pvLookupDict["transmissionSet"], self, 60, 3 - ) - colTransmissionLabel = QtWidgets.QLabel("Transmission (0.0-1.0):") - self.transmissionReadback_ledit = self.transmissionReadback.getEntry() - - colTransmissionLabel.setAlignment(QtCore.Qt.AlignCenter) - colTransmissionLabel.setFixedWidth(190) - - transmisionSPLabel = QtWidgets.QLabel("SetPoint:") - - self.transmission_ledit = self.transmissionSetPoint.getEntry() - self.transmission_ledit.setValidator( - QtGui.QDoubleValidator( - VALID_TRANSMISSION[daq_utils.beamline]["min"], - VALID_TRANSMISSION[daq_utils.beamline]["max"], - VALID_TRANSMISSION[daq_utils.beamline]["digits"], - ) - ) - self.setGuiValues({"transmission": getBlConfig("stdTrans")}) - self.transmission_ledit.returnPressed.connect(self.setTransCB) - if daq_utils.beamline == "fmx": - self.transmission_ledit.textChanged.connect(self.calcLifetimeCB) - setTransButton = QtWidgets.QPushButton("Set Trans") - setTransButton.clicked.connect(self.setTransCB) - beamsizeLabel = QtWidgets.QLabel("BeamSize:") - if daq_utils.beamline == "nyx": - # beamSizeOptionList = self.aperture.get_diameter_list() PV not working, needs investigation - beamSizeOptionList = ["10", "20", "30", "50", "100"] - current_index = self.aperture.current_index.get() - else: - beamSizeOptionList = ["V0H0", "V0H1", "V1H0", "V1H1"] - current_index = int(self.beamSize_pv.get()) - self.beamsizeComboBox = QtWidgets.QComboBox(self) - self.beamsizeComboBox.addItems(beamSizeOptionList) - self.beamsizeComboBox.setCurrentIndex(current_index) - self.beamsizeComboBox.activated[str].connect(self.beamsizeComboActivatedCB) - if daq_utils.beamline == "amx" or self.energy_pv.get() < 9000: - self.beamsizeComboBox.setEnabled(False) - hBoxColParams3 = QtWidgets.QHBoxLayout() - colEnergyLabel = QtWidgets.QLabel("Energy (eV):") - colEnergyLabel.setAlignment(QtCore.Qt.AlignCenter) - self.energyMotorEntry = QtEpicsPVLabel( - daq_utils.motor_dict["energy"] + ".RBV", self, 70, 2 - ) - self.energyReadback = self.energyMotorEntry.getEntry() - energySPLabel = QtWidgets.QLabel("SetPoint:") - self.energyMoveLedit = QtEpicsPVEntry( - daq_utils.motor_dict["energy"] + ".VAL", self, 75, 2 - ) - self.energy_ledit = self.energyMoveLedit.getEntry() - self.energy_ledit.setValidator(QtGui.QDoubleValidator()) - self.energy_ledit.returnPressed.connect(self.moveEnergyCB) - moveEnergyButton = QtWidgets.QPushButton("Move Energy") - moveEnergyButton.clicked.connect(self.moveEnergyCB) - #hBoxColParams3.addWidget(colEnergyLabel) - #hBoxColParams3.addWidget(self.energyReadback) - #hBoxColParams3.addWidget(energySPLabel) - #hBoxColParams3.addWidget(self.energy_ledit) - #hBoxColParams22.addWidget(colTransmissionLabel) - #hBoxColParams22.addWidget(self.transmissionReadback_ledit) - #hBoxColParams22.addWidget(transmisionSPLabel) - #hBoxColParams22.addWidget(self.transmission_ledit) - #hBoxColParams22.insertSpacing(5, 100) - #hBoxColParams22.addWidget(beamsizeLabel) - #hBoxColParams22.addWidget(self.beamsizeComboBox) - hBoxColParams4 = QtWidgets.QHBoxLayout() - colBeamWLabel = QtWidgets.QLabel("Beam Width:") - colBeamWLabel.setFixedWidth(140) - colBeamWLabel.setAlignment(QtCore.Qt.AlignCenter) - self.beamWidth_ledit = QtWidgets.QLineEdit() - self.beamWidth_ledit.setFixedWidth(60) - self.beamWidth_ledit.setText(getBlConfig("screen_default_beamWidth")) - colBeamHLabel = QtWidgets.QLabel("Beam Height:") - colBeamHLabel.setFixedWidth(140) - colBeamHLabel.setAlignment(QtCore.Qt.AlignCenter) - self.beamHeight_ledit = QtWidgets.QLineEdit() - self.beamHeight_ledit.setFixedWidth(60) - self.beamHeight_ledit.setText(getBlConfig("screen_default_beamHeight")) - hBoxColParams4.addWidget(colBeamWLabel) - hBoxColParams4.addWidget(self.beamWidth_ledit) - hBoxColParams4.addWidget(colBeamHLabel) - hBoxColParams4.addWidget(self.beamHeight_ledit) - hBoxColParams5 = QtWidgets.QHBoxLayout() - colResoLabel = QtWidgets.QLabel("Edge Resolution:") - colResoLabel.setAlignment(QtCore.Qt.AlignCenter) - self.resolution_ledit = QtWidgets.QLineEdit() - self.resolution_ledit.setFixedWidth(60) - self.resolution_ledit.setValidator(QtGui.QDoubleValidator()) - self.resolution_ledit.textEdited[str].connect(self.resoTextChanged) - if daq_utils.beamline == "nyx": - self.resolution_ledit.setEnabled(False) - detDistLabel = QtWidgets.QLabel("Detector Dist.") - #detDistLabel.setAlignment(QtCore.Qt.AlignCenter) - detDistRBLabel = QtWidgets.QLabel("Readback:") - self.detDistRBVLabel = QtEpicsPVLabel( - daq_utils.motor_dict["detectorDist"] + ".RBV", self, 70 - ) - detDistSPLabel = QtWidgets.QLabel("SetPoint:") - self.detDistMotorEntry = QtEpicsPVEntry( - daq_utils.motor_dict["detectorDist"] + ".VAL", self, 70, 2 - ) - self.detDistMotorEntry.getEntry().setValidator( - QtGui.QDoubleValidator( - VALID_DET_DIST[daq_utils.beamline]["min"], - VALID_DET_DIST[daq_utils.beamline]["max"], - VALID_DET_DIST[daq_utils.beamline]["digits"], - ) - ) - self.detDistMotorEntry.getEntry().textChanged[str].connect( - self.detDistTextChanged - ) - self.detDistMotorEntry.getEntry().textChanged[str].connect(self.checkEntryState) - self.detDistMotorEntry.getEntry().returnPressed.connect(self.moveDetDistCB) - self.moveDetDistButton = QtWidgets.QPushButton("Move Detector") - self.moveDetDistButton.clicked.connect(self.moveDetDistCB) - #hBoxColParams3.addWidget(detDistLabel) - #hBoxColParams3.addWidget(self.detDistRBVLabel.getEntry()) - #hBoxColParams3.addWidget(detDistSPLabel) - #hBoxColParams3.addWidget(self.detDistMotorEntry.getEntry()) - hBoxColParams6 = QtWidgets.QHBoxLayout() - hBoxColParams6.setAlignment(QtCore.Qt.AlignLeft) - hBoxColParams7 = QtWidgets.QHBoxLayout() - hBoxColParams7.setAlignment(QtCore.Qt.AlignLeft) - centeringLabel = QtWidgets.QLabel("Sample Centering:") - centeringLabel.setFixedWidth(140) - centeringOptionList = ["Interactive", "AutoLoop", "AutoRaster", "Testing"] - self.centeringComboBox = QtWidgets.QComboBox(self) - self.centeringComboBox.addItems(centeringOptionList) - protoLabel = QtWidgets.QLabel("Protocol:") - font = QtGui.QFont() - font.setBold(True) - protoLabel.setFont(font) - protoLabel.setAlignment(QtCore.Qt.AlignCenter) - self.protoRadioGroup = QtWidgets.QButtonGroup() - self.protoStandardRadio = QtWidgets.QRadioButton("standard") - self.protoStandardRadio.setChecked(True) - self.protoStandardRadio.toggled.connect( - functools.partial(self.protoRadioToggledCB, "standard") - ) - self.protoStandardRadio.pressed.connect( - functools.partial(self.protoRadioToggledCB, "standard") - ) - self.protoRadioGroup.addButton(self.protoStandardRadio) - self.protoRasterRadio = QtWidgets.QRadioButton("raster") - self.protoRasterRadio.toggled.connect( - functools.partial(self.protoRadioToggledCB, "raster") - ) - self.protoRasterRadio.pressed.connect( - functools.partial(self.protoRadioToggledCB, "raster") - ) - self.protoRadioGroup.addButton(self.protoRasterRadio) - self.protoVectorRadio = QtWidgets.QRadioButton("vector") - self.protoRasterRadio.toggled.connect( - functools.partial(self.protoRadioToggledCB, "vector") - ) - self.protoRasterRadio.pressed.connect( - functools.partial(self.protoRadioToggledCB, "vector") - ) - self.protoRadioGroup.addButton(self.protoVectorRadio) - self.protoOtherRadio = QtWidgets.QRadioButton("other") - self.protoOtherRadio.setEnabled(False) - self.protoRadioGroup.addButton(self.protoOtherRadio) - if daq_utils.beamline == "nyx": - protoOptionList = ["standard","raster","vector"] # these should probably come from db - - else: - protoOptionList = [ - "standard", - "raster", - "vector", - "burn", - "rasterScreen", - "stepRaster", - "stepVector", - "multiCol", - "characterize", - "ednaCol", - ] # these should probably come from db - self.protoComboBox = QtWidgets.QComboBox(self) - self.protoComboBox.addItems(protoOptionList) - self.protoComboBox.activated[str].connect(self.protoComboActivatedCB) - hBoxColParams6.addWidget(protoLabel) - hBoxColParams6.addWidget(self.protoStandardRadio) - hBoxColParams6.addWidget(self.protoRasterRadio) - hBoxColParams6.addWidget(self.protoVectorRadio) - hBoxColParams6.addWidget(self.protoComboBox) - hBoxColParams7.addWidget(centeringLabel) - hBoxColParams7.addWidget(self.centeringComboBox) - #hBoxColParams7.addWidget(colResoLabel) - #hBoxColParams7.addWidget(self.resolution_ledit) - self.processingOptionsFrame = QFrame() - self.hBoxProcessingLayout1 = QtWidgets.QHBoxLayout() - self.hBoxProcessingLayout1.setAlignment(QtCore.Qt.AlignLeft) - procOptionLabel = QtWidgets.QLabel("Processing Options:") - procOptionLabel.setFixedWidth(200) - self.autoProcessingCheckBox = QCheckBox("AutoProcessing On") - self.autoProcessingCheckBox.setChecked(True) - self.autoProcessingCheckBox.stateChanged.connect(self.autoProcessingCheckCB) - self.fastEPCheckBox = QCheckBox("FastEP") - self.fastEPCheckBox.setChecked(False) - self.fastEPCheckBox.setEnabled(False) - self.dimpleCheckBox = QCheckBox("Dimple") - self.dimpleCheckBox.setChecked(True) - self.xia2CheckBox = QCheckBox("Xia2") - self.xia2CheckBox.setChecked(False) - self.hBoxProcessingLayout1.addWidget(self.autoProcessingCheckBox) - self.hBoxProcessingLayout1.addWidget(self.fastEPCheckBox) - self.hBoxProcessingLayout1.addWidget(self.dimpleCheckBox) - self.processingOptionsFrame.setLayout(self.hBoxProcessingLayout1) - self.rasterParamsFrame = QFrame() - self.vBoxRasterParams = QtWidgets.QVBoxLayout() - self.hBoxRasterLayout1 = QtWidgets.QHBoxLayout() - self.hBoxRasterLayout1.setAlignment(QtCore.Qt.AlignLeft) - self.hBoxRasterLayout2 = QtWidgets.QHBoxLayout() - self.hBoxRasterLayout2.setAlignment(QtCore.Qt.AlignLeft) - rasterStepLabel = QtWidgets.QLabel("Raster Step") - rasterStepLabel.setFixedWidth(110) - self.rasterStepEdit = QtWidgets.QLineEdit(str(self.rasterStepDefs["Coarse"])) - self.rasterStepEdit.textChanged[str].connect(self.rasterStepChanged) - self.rasterStepEdit.setFixedWidth(60) - self.rasterGrainRadioGroup = QtWidgets.QButtonGroup() - self.rasterGrainCoarseRadio = QtWidgets.QRadioButton("Coarse") - self.rasterGrainCoarseRadio.setChecked(False) - self.rasterGrainCoarseRadio.toggled.connect( - functools.partial(self.rasterGrainToggledCB, "Coarse") - ) - self.rasterGrainRadioGroup.addButton(self.rasterGrainCoarseRadio) - self.rasterGrainFineRadio = QtWidgets.QRadioButton("Fine") - self.rasterGrainFineRadio.setChecked(False) - self.rasterGrainFineRadio.toggled.connect( - functools.partial(self.rasterGrainToggledCB, "Fine") - ) - self.rasterGrainRadioGroup.addButton(self.rasterGrainFineRadio) - self.rasterGrainVFineRadio = QtWidgets.QRadioButton("VFine") - self.rasterGrainVFineRadio.setChecked(False) - self.rasterGrainVFineRadio.toggled.connect( - functools.partial(self.rasterGrainToggledCB, "VFine") - ) - self.rasterGrainRadioGroup.addButton(self.rasterGrainVFineRadio) - self.rasterGrainCustomRadio = QtWidgets.QRadioButton("Custom") - self.rasterGrainCustomRadio.setChecked(True) - self.rasterGrainCustomRadio.toggled.connect( - functools.partial(self.rasterGrainToggledCB, "Custom") - ) - self.rasterGrainRadioGroup.addButton(self.rasterGrainCustomRadio) - rasterEvalLabel = QtWidgets.QLabel("Raster\nEvaluate By:") - rasterEvalOptionList = ["Spot Count", "Resolution", "Intensity"] - self.rasterEvalComboBox = QtWidgets.QComboBox(self) - self.rasterEvalComboBox.addItems(rasterEvalOptionList) - self.rasterEvalComboBox.setCurrentIndex( - db_lib.beamlineInfo(daq_utils.beamline, "rasterScoreFlag")["index"] - ) - self.rasterEvalComboBox.activated[str].connect(self.rasterEvalComboActivatedCB) - self.hBoxRasterLayout1.addWidget(rasterStepLabel) - self.hBoxRasterLayout1.addWidget(self.rasterStepEdit) - self.hBoxRasterLayout1.addWidget(self.rasterGrainCoarseRadio) - self.hBoxRasterLayout1.addWidget(self.rasterGrainFineRadio) - self.hBoxRasterLayout1.addWidget(self.rasterGrainVFineRadio) - self.hBoxRasterLayout1.addWidget(self.rasterGrainCustomRadio) - self.hBoxRasterLayout1.addWidget(rasterEvalLabel) - self.hBoxRasterLayout1.addWidget(self.rasterEvalComboBox) - self.vBoxRasterParams.addLayout(self.hBoxRasterLayout1) - self.vBoxRasterParams.addLayout(self.hBoxRasterLayout2) - self.rasterParamsFrame.setLayout(self.vBoxRasterParams) - self.multiColParamsFrame = ( - QFrame() - ) # something for criteria to decide on which hotspots to collect on for multi-xtal - self.hBoxMultiColParamsLayout1 = QtWidgets.QHBoxLayout() - self.hBoxMultiColParamsLayout1.setAlignment(QtCore.Qt.AlignLeft) - multiColCutoffLabel = QtWidgets.QLabel("Diffraction Cutoff") - multiColCutoffLabel.setFixedWidth(110) - self.multiColCutoffEdit = QtWidgets.QLineEdit( - "320" - ) # may need to store this in DB at some point, it's a silly number for now - self.multiColCutoffEdit.setFixedWidth(60) - self.hBoxMultiColParamsLayout1.addWidget(multiColCutoffLabel) - self.hBoxMultiColParamsLayout1.addWidget(self.multiColCutoffEdit) - self.multiColParamsFrame.setLayout(self.hBoxMultiColParamsLayout1) - self.characterizeParamsFrame = QFrame() - vBoxCharacterizeParams1 = QtWidgets.QVBoxLayout() - self.hBoxCharacterizeLayout1 = QtWidgets.QHBoxLayout() - self.characterizeTargetLabel = QtWidgets.QLabel("Characterization Targets") - characterizeResoLabel = QtWidgets.QLabel("Resolution") - characterizeResoLabel.setAlignment(QtCore.Qt.AlignCenter) - self.characterizeResoEdit = QtWidgets.QLineEdit("3.0") - characterizeISIGLabel = QtWidgets.QLabel("I/Sigma") - characterizeISIGLabel.setAlignment(QtCore.Qt.AlignCenter) - self.characterizeISIGEdit = QtWidgets.QLineEdit("2.0") - self.characterizeAnomCheckBox = QCheckBox("Anomolous") - self.characterizeAnomCheckBox.setChecked(False) - self.hBoxCharacterizeLayout2 = QtWidgets.QHBoxLayout() - characterizeCompletenessLabel = QtWidgets.QLabel("Completeness") - characterizeCompletenessLabel.setAlignment(QtCore.Qt.AlignCenter) - self.characterizeCompletenessEdit = QtWidgets.QLineEdit("0.99") - characterizeMultiplicityLabel = QtWidgets.QLabel("Multiplicity") - characterizeMultiplicityLabel.setAlignment(QtCore.Qt.AlignCenter) - self.characterizeMultiplicityEdit = QtWidgets.QLineEdit("auto") - characterizeDoseLimitLabel = QtWidgets.QLabel("Dose Limit") - characterizeDoseLimitLabel.setAlignment(QtCore.Qt.AlignCenter) - self.characterizeDoseLimitEdit = QtWidgets.QLineEdit("100") - characterizeSpaceGroupLabel = QtWidgets.QLabel("Space Group") - characterizeSpaceGroupLabel.setAlignment(QtCore.Qt.AlignCenter) - self.characterizeSpaceGroupEdit = QtWidgets.QLineEdit("P1") - self.hBoxCharacterizeLayout1.addWidget(characterizeResoLabel) - self.hBoxCharacterizeLayout1.addWidget(self.characterizeResoEdit) - self.hBoxCharacterizeLayout1.addWidget(characterizeISIGLabel) - self.hBoxCharacterizeLayout1.addWidget(self.characterizeISIGEdit) - self.hBoxCharacterizeLayout1.addWidget(characterizeSpaceGroupLabel) - self.hBoxCharacterizeLayout1.addWidget(self.characterizeSpaceGroupEdit) - self.hBoxCharacterizeLayout1.addWidget(self.characterizeAnomCheckBox) - self.hBoxCharacterizeLayout2.addWidget(characterizeCompletenessLabel) - self.hBoxCharacterizeLayout2.addWidget(self.characterizeCompletenessEdit) - self.hBoxCharacterizeLayout2.addWidget(characterizeMultiplicityLabel) - self.hBoxCharacterizeLayout2.addWidget(self.characterizeMultiplicityEdit) - self.hBoxCharacterizeLayout2.addWidget(characterizeDoseLimitLabel) - self.hBoxCharacterizeLayout2.addWidget(self.characterizeDoseLimitEdit) - vBoxCharacterizeParams1.addWidget(self.characterizeTargetLabel) - vBoxCharacterizeParams1.addLayout(self.hBoxCharacterizeLayout1) - vBoxCharacterizeParams1.addLayout(self.hBoxCharacterizeLayout2) - self.characterizeParamsFrame.setLayout(vBoxCharacterizeParams1) - self.vectorParamsFrame = QFrame() - hBoxVectorLayout1 = QtWidgets.QHBoxLayout() - setVectorStartButton = QtWidgets.QPushButton("Vector\nStart") - setVectorStartButton.setStyleSheet("background-color: blue") - setVectorStartButton.clicked.connect( - lambda: self.setVectorPointCB("vectorStart") - ) - setVectorEndButton = QtWidgets.QPushButton("Vector\nEnd") - setVectorEndButton.setStyleSheet("background-color: red") - setVectorEndButton.clicked.connect(lambda: self.setVectorPointCB("vectorEnd")) - self.vecLine = None - vectorFPPLabel = QtWidgets.QLabel("Number of Wedges") - self.vectorFPP_ledit = QtWidgets.QLineEdit("1") - self.vectorFPP_ledit.setValidator(QIntValidator(self)) - vecLenLabel = QtWidgets.QLabel(" Length(microns):") - self.vecLenLabelOutput = QtWidgets.QLabel("---") - vecSpeedLabel = QtWidgets.QLabel(" Speed(microns/s):") - self.vecSpeedLabelOutput = QtWidgets.QLabel("---") - hBoxVectorLayout1.addWidget(setVectorStartButton) - hBoxVectorLayout1.addWidget(setVectorEndButton) - hBoxVectorLayout1.addWidget(vectorFPPLabel) - hBoxVectorLayout1.addWidget(self.vectorFPP_ledit) - hBoxVectorLayout1.addWidget(vecLenLabel) - hBoxVectorLayout1.addWidget(self.vecLenLabelOutput) - hBoxVectorLayout1.addWidget(vecSpeedLabel) - hBoxVectorLayout1.addWidget(self.vecSpeedLabelOutput) - self.vectorParamsFrame.setLayout(hBoxVectorLayout1) - vBoxColParams1.addLayout(hBoxColParams1) - vBoxColParams1.addLayout(hBoxColParams2) - vBoxColParams1.addLayout(hBoxColParams25) - vBoxColParams1.addLayout(hBoxColParams22) - vBoxColParams1.addLayout(hBoxColParams3) - #vBoxColParams1.addLayout(hBoxColParams7) - #vBoxColParams1.addLayout(hBoxColParams6) - #vBoxColParams1.addWidget(self.rasterParamsFrame) - #vBoxColParams1.addWidget(self.multiColParamsFrame) - #vBoxColParams1.addWidget(self.vectorParamsFrame) - #vBoxColParams1.addWidget(self.characterizeParamsFrame) - #vBoxColParams1.addWidget(self.processingOptionsFrame) - mikesGB = QtWidgets.QGroupBox() - mikesGB.setTitle("Acquisition") - - paramSubspace = QtWidgets.QGridLayout() - - #paramSubspace.setColumnMinimumWidth(0, 140) - #paramSubspace.setColumnMinimumWidth(1, 90) - #paramSubspace.setColumnMinimumWidth(2, 140) - #paramSubspace.setColumnMinimumWidth(3, 90) - #paramSubspace.setColumnMinimumWidth(4, 90) - #paramSubspace.setColumnStretch(1) learn about stretch factor and then update - - - # Parameter Collection Column 1, Labels - colStartLabel.setAlignment(QtCore.Qt.AlignLeft) - paramSubspace.addWidget(colStartLabel,1,0, alignment=QtCore.Qt.AlignLeft) - self.colEndLabel.setAlignment(QtCore.Qt.AlignLeft) - paramSubspace.addWidget(self.colEndLabel,2,0, alignment=QtCore.Qt.AlignLeft) - colRangeLabel.setAlignment(QtCore.Qt.AlignLeft) - paramSubspace.addWidget(colRangeLabel,0,0, alignment=QtCore.Qt.AlignLeft) - colExptimeLabel.setAlignment(QtCore.Qt.AlignLeft) - paramSubspace.addWidget(colExptimeLabel,3,0, alignment=QtCore.Qt.AlignLeft) - totalExptimeLabel.setAlignment(QtCore.Qt.AlignLeft) - paramSubspace.addWidget(totalExptimeLabel,4,0, alignment=QtCore.Qt.AlignLeft) - # Parameter Collection Column 2, Input Boxes - paramSubspace.addWidget(self.osc_start_ledit,1,1, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(self.osc_end_ledit,2,1, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(self.osc_range_ledit,0,1, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(self.exp_time_ledit,3,1, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(self.totalExptime_ledit,4,1, alignment=QtCore.Qt.AlignLeft) - # Parameter Collection Column 3, Labels - paramSubspace.addWidget(detDistLabel,0,2, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(colResoLabel,1,2, alignment=QtCore.Qt.AlignLeft) - #paramSubspace.addWidget(detDistSPLabel,1,2, alignment=QtCore.Qt.AlignLeft) - #hBoxColParams7.addWidget(colResoLabel) - paramSubspace.addWidget(colEnergyLabel,2,2, alignment=QtCore.Qt.AlignLeft) - #paramSubspace.addWidget(energySPLabel,3,2, alignment=QtCore.Qt.AlignLeft) - colTransmissionLabel.setAlignment(QtCore.Qt.AlignLeft) - paramSubspace.addWidget(colTransmissionLabel,3,2, alignment=QtCore.Qt.AlignLeft) - transmisionSPLabel.setAlignment(QtCore.Qt.AlignLeft) - #paramSubspace.addWidget(transmisionSPLabel,3,2, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(beamsizeLabel,4,2, alignment=QtCore.Qt.AlignLeft) - # Parameter Collection Column 4, Input Boxes - paramSubspace.addWidget(self.detDistMotorEntry.getEntry(),0,3, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(self.resolution_ledit,1,3, alignment=QtCore.Qt.AlignLeft) - #hBoxColParams7.addWidget(self.resolution_ledit) - paramSubspace.addWidget(self.energy_ledit,2,3, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(self.transmission_ledit,3,3, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(self.beamsizeComboBox,4,3, alignment=QtCore.Qt.AlignLeft) - # Param Collection Column 5, RBV - paramSubspace.addWidget(self.energyReadback,2,4, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(self.detDistRBVLabel.getEntry(),0,4, alignment=QtCore.Qt.AlignLeft) - paramSubspace.addWidget(self.transmissionReadback_ledit,3,4, alignment=QtCore.Qt.AlignLeft) - - improvedParamSpacing = QtWidgets.QVBoxLayout() - improvedParamSpacing.addWidget(self.stillModeCheckBox) - improvedParamSpacing.addLayout(paramSubspace) - improvedParamSpacing.addLayout(hBoxColParams7) - improvedParamSpacing.addLayout(hBoxColParams6) - improvedParamSpacing.addWidget(self.rasterParamsFrame) - improvedParamSpacing.addWidget(self.multiColParamsFrame) - improvedParamSpacing.addWidget(self.vectorParamsFrame) - improvedParamSpacing.addWidget(self.characterizeParamsFrame) - improvedParamSpacing.addWidget(self.processingOptionsFrame) - mikesGB.setLayout(improvedParamSpacing) - - self.rasterParamsFrame.hide() - self.multiColParamsFrame.hide() - self.characterizeParamsFrame.hide() - colParamsGB.setLayout(vBoxColParams1) - self.dataPathGB = DataLocInfo(self) - hBoxDisplayOptionLayout= QtWidgets.QHBoxLayout() - self.albulaDispCheckBox = QCheckBox("Display Data (Albula)") - self.albulaDispCheckBox.setChecked(False) - hBoxDisplayOptionLayout.addWidget(self.albulaDispCheckBox) - #vBoxMainColLayout.addWidget(colParamsGB) - vBoxMainColLayout.addWidget(mikesGB) - - vBoxMainColLayout.addWidget(self.dataPathGB) - self.mainColFrame.setLayout(vBoxMainColLayout) - self.mainToolBox.addItem(self.mainColFrame, "Collection Parameters") - editSampleButton = QtWidgets.QPushButton("Apply Changes") - editSampleButton.clicked.connect(self.editSelectedRequestsCB) - cloneRequestButton = QtWidgets.QPushButton("Clone Raster Request") - cloneRequestButton.clicked.connect(self.cloneRequestCB) - hBoxPriorityLayout1 = QtWidgets.QHBoxLayout() - priorityEditLabel = QtWidgets.QLabel("Priority Edit") - priorityTopButton = QtWidgets.QPushButton(" >> ") - priorityUpButton = QtWidgets.QPushButton(" > ") - priorityDownButton = QtWidgets.QPushButton(" < ") - priorityBottomButton = QtWidgets.QPushButton(" << ") - priorityTopButton.clicked.connect(self.topPriorityCB) - priorityBottomButton.clicked.connect(self.bottomPriorityCB) - priorityUpButton.clicked.connect(self.upPriorityCB) - priorityDownButton.clicked.connect(self.downPriorityCB) - hBoxPriorityLayout1.addWidget(priorityEditLabel) - hBoxPriorityLayout1.addWidget(priorityBottomButton) - hBoxPriorityLayout1.addWidget(priorityDownButton) - hBoxPriorityLayout1.addWidget(priorityUpButton) - hBoxPriorityLayout1.addWidget(priorityTopButton) - queueSampleButton = QtWidgets.QPushButton("Add Requests to Queue") - queueSampleButton.clicked.connect(self.addRequestsToAllSelectedCB) - deleteSampleButton = QtWidgets.QPushButton("Delete Requests") - deleteSampleButton.clicked.connect( - functools.partial(self.dewarTree.deleteSelectedCB, 0) - ) - editScreenParamsButton = QtWidgets.QPushButton("Edit Raster Params...") - editScreenParamsButton.clicked.connect(self.editScreenParamsCB) - vBoxMainSetup.addWidget(self.mainToolBox) - vBoxMainSetup.addLayout(hBoxPriorityLayout1) - vBoxMainSetup.addWidget(queueSampleButton) - vBoxMainSetup.addWidget(editSampleButton) - vBoxMainSetup.addWidget(cloneRequestButton) - - vBoxMainSetup.addWidget(editScreenParamsButton) - self.mainSetupFrame.setLayout(vBoxMainSetup) - self.VidFrame = QFrame() - self.VidFrame.setFixedWidth(680) - vBoxVidLayout = QtWidgets.QVBoxLayout() - self.captureLowMag = None - self.captureHighMag = None - self.captureHighMagZoom = None - self.captureLowMagZoom = None - if daq_utils.has_xtalview: - if self.zoom3FrameRatePV.get() != 0: - _thread.start_new_thread(self.initVideo2, (0.25,)) # highMag - if self.zoom4FrameRatePV.get() != 0: - _thread.start_new_thread( - self.initVideo4, (0.25,) - ) # this sets up highMagDigiZoom - if self.zoom2FrameRatePV.get() != 0: - _thread.start_new_thread( - self.initVideo3, (0.25,) - ) # this sets up lowMagDigiZoom - if self.zoom1FrameRatePV.get() != 0: - #self.captureLowMag = cv2.VideoCapture(daq_utils.lowMagCamURL) - logger.debug('lowMagCamURL: "' + daq_utils.lowMagCamURL + '"') - - #self.captureLowMag = cv2.VideoCapture(daq_utils.lowMagCamURL) - self.capture = self.captureLowMag - #self.frame_queue = Queue() - #self.active_camera_threads = [] - self.timerSample = QTimer() - #self.timerSample.timeout.connect(self.sampleFrameCB) - #self.timerSample.timeout.connect(self.timerSampleRefresh) - #self.timerSample.start(SAMPLE_TIMER_DELAY) - - self.centeringMarksList = [] - self.rasterList = [] - self.rasterDefList = [] - self.polyPointItems = [] - self.rasterPoly = None - self.measureLine = None - self.scene = QtWidgets.QGraphicsScene(0, 0, 640, 512, self) - hBoxHutchVidsLayout = QtWidgets.QHBoxLayout() - self.sceneHutchCorner = QtWidgets.QGraphicsScene(0, 0, 320, 180, self) - self.sceneHutchTop = QtWidgets.QGraphicsScene(0, 0, 320, 180, self) - self.scene.keyPressEvent = self.sceneKey - self.view = QtWidgets.QGraphicsView(self.scene) - self.viewHutchCorner = QtWidgets.QGraphicsView(self.sceneHutchCorner) - self.viewHutchTop = QtWidgets.QGraphicsView(self.sceneHutchTop) - self.pixmap_item = QtWidgets.QGraphicsPixmapItem(None) - self.scene.addItem(self.pixmap_item) - self.pixmap_item_HutchCorner = QtWidgets.QGraphicsPixmapItem(None) - self.sceneHutchCorner.addItem(self.pixmap_item_HutchCorner) - self.pixmap_item_HutchTop = QtWidgets.QGraphicsPixmapItem(None) - self.sceneHutchTop.addItem(self.pixmap_item_HutchTop) - - self.pixmap_item.mousePressEvent = self.pixelSelect - try: - if QtGui.QColor.isValidColor(getBlConfig("defaultOverlayColor")): - centerMarkBrush = QtGui.QBrush( - QtGui.QColor(getBlConfig("defaultOverlayColor")) - ) - else: - centerMarkBrush = QtGui.QBrush(QtCore.Qt.blue) - except KeyError as e: - logger.warning("No value found for defaultOverlayColor") - centerMarkBrush = QtGui.QBrush(QtCore.Qt.blue) - centerMarkPen = QtGui.QPen(centerMarkBrush, 2.0) - self.centerMarker = QtWidgets.QGraphicsSimpleTextItem("+") - self.centerMarker.setZValue(10.0) - self.centerMarker.setBrush(centerMarkBrush) - font = QtGui.QFont("DejaVu Sans Light", self.centerMarkerCharSize, weight=0) - self.centerMarker.setFont(font) - self.scene.addItem(self.centerMarker) - self.centerMarker.setPos( - self.getMD2BeamCenterX() - self.centerMarkerCharOffsetX, - self.getMD2BeamCenterY() - self.centerMarkerCharOffsetY, - ) - self.zoomRadioGroup = QtWidgets.QButtonGroup() - self.zoom1Radio = QtWidgets.QRadioButton("Mag1") - self.zoom1Radio.setChecked(True) - self.zoom1Radio.toggled.connect( - functools.partial(self.zoomLevelToggledCB, "Zoom1") - ) - self.zoomRadioGroup.addButton(self.zoom1Radio) - self.zoom2Radio = QtWidgets.QRadioButton("Mag2") - self.zoom2Radio.toggled.connect( - functools.partial(self.zoomLevelToggledCB, "Zoom2") - ) - self.zoom3Radio = QtWidgets.QRadioButton("Mag3") - self.zoom3Radio.toggled.connect( - functools.partial(self.zoomLevelToggledCB, "Zoom3") - ) - self.zoom4Radio = QtWidgets.QRadioButton("Mag4") - self.zoom4Radio.toggled.connect( - functools.partial(self.zoomLevelToggledCB, "Zoom4") - ) - if daq_utils.sampleCameraCount >= 2: - self.zoomRadioGroup.addButton(self.zoom2Radio) - if daq_utils.sampleCameraCount >= 3: - self.zoomRadioGroup.addButton(self.zoom3Radio) - if daq_utils.sampleCameraCount >= 4: - self.zoomRadioGroup.addButton(self.zoom4Radio) - else: - self.zoomLevelComboBox = QtWidgets.QComboBox(self) - self.zoomLevelComboBox.addItems(["1","2","3","4","5","6","7"]) - self.zoomLevelComboBox.activated[str].connect(self.zoomLevelComboActivatedCB) - self.zoom1Radio.hide() - self.zoom2Radio.hide() - self.zoom3Radio.hide() - self.zoom4Radio.hide() - hBoxZoomLevelLayout = QtWidgets.QHBoxLayout() - hBoxZoomLevelLayout.addWidget(self.zoomLevelComboBox) - - - beamOverlayPen = QtGui.QPen(QtCore.Qt.red) - self.tempBeamSizeXMicrons = 30 - self.tempBeamSizeYMicrons = 30 - self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) - self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) - self.overlayPosOffsetX = self.centerMarkerCharOffsetX - 1 - self.overlayPosOffsetY = self.centerMarkerCharOffsetY - 1 - self.beamSizeOverlay = QtWidgets.QGraphicsRectItem( - self.centerMarker.x() - self.overlayPosOffsetX, - self.centerMarker.y() - self.overlayPosOffsetY, - self.beamSizeXPixels, - self.beamSizeYPixels, - ) - self.beamSizeOverlay.setPen(beamOverlayPen) - self.scene.addItem(self.beamSizeOverlay) - self.beamSizeOverlay.setVisible(False) - self.beamSizeOverlay.setRect( - self.overlayPosOffsetX + self.centerMarker.x() - (self.beamSizeXPixels / 2), - self.overlayPosOffsetY + self.centerMarker.y() - (self.beamSizeYPixels / 2), - self.beamSizeXPixels, - self.beamSizeYPixels, - ) - try: - if QtGui.QColor.isValidColor(getBlConfig("defaultOverlayColor")): - scaleBrush = QtGui.QBrush( - QtGui.QColor(getBlConfig("defaultOverlayColor")) - ) - else: - scaleBrush = QtGui.QBrush(QtCore.Qt.blue) - except KeyError as e: - logger.warning("No value found for defaultOverlayColor") - scaleBrush = QtGui.QBrush(QtCore.Qt.blue) - scalePen = QtGui.QPen(scaleBrush, 2.0) - scaleTextPen = QtGui.QPen(scaleBrush, 1.0) - self.imageScaleLineLen = 50 - self.imageScale = self.scene.addLine( - 10, - daq_utils.screenPixY - 30, - 10 + self.imageScaleLineLen, - daq_utils.screenPixY - 30, - scalePen, - ) - self.imageScaleText = self.scene.addSimpleText( - "50 microns", font=QtGui.QFont("Times", 13) - ) - self.imageScaleText.setPen(scaleTextPen) - self.imageScaleText.setPos(10, 450) - self.click_positions = [] - hBoxHutchVidsLayout.addWidget(self.viewHutchTop) - hBoxHutchVidsLayout.addWidget(self.viewHutchCorner) - vBoxVidLayout.addLayout(hBoxHutchVidsLayout) - vBoxVidLayout.addWidget(self.view) - hBoxSampleOrientationLayout = QtWidgets.QHBoxLayout() - setDC2CPButton = QtWidgets.QPushButton("SetStart") - setDC2CPButton.setFixedWidth(50) - setDC2CPButton.clicked.connect(self.setDCStartCB) - omegaLabel = QtWidgets.QLabel("Omega:") - #omegaMonitorPV = str(getBlConfig("omegaMonitorPV")) - self.sampleOmegaRBVLedit = QtEpicsPVLabel( - self.gon.omega.readback.pvname, self, 70 - ) - omegaSPLabel = QtWidgets.QLabel("SetPoint:") - omegaSPLabel.setFixedWidth(70) - self.sampleOmegaMoveLedit = QtEpicsPVEntry( - self.gon.omega.setpoint.pvname, self, 70, 2 - ) - self.sampleOmegaMoveLedit.getEntry().returnPressed.connect(self.moveOmegaCB) - moveOmegaButton = QtWidgets.QPushButton("Move") - moveOmegaButton.clicked.connect(self.moveOmegaCB) - omegaTweakNegButtonSuperFine = QtWidgets.QPushButton("-1") - omegaTweakNegButtonFine = QtWidgets.QPushButton("-5") - omegaTweakNegButton = QtWidgets.QPushButton("<") - omegaTweakNegButton.clicked.connect(self.omegaTweakNegCB) - omegaTweakNegButtonFine.clicked.connect( - functools.partial(self.omegaTweakCB, -5) - ) - omegaTweakNegButtonSuperFine.clicked.connect( - functools.partial(self.omegaTweakCB, -1) - ) - self.omegaTweakVal_ledit = QtWidgets.QLineEdit() - self.omegaTweakVal_ledit.setFixedWidth(45) - self.omegaTweakVal_ledit.setText("90") - omegaTweakPosButtonSuperFine = QtWidgets.QPushButton("+1") - omegaTweakPosButtonFine = QtWidgets.QPushButton("+5") - omegaTweakPosButton = QtWidgets.QPushButton(">") - omegaTweakPosButton.clicked.connect(self.omegaTweakPosCB) - omegaTweakPosButtonFine.clicked.connect(functools.partial(self.omegaTweakCB, 5)) - omegaTweakPosButtonSuperFine.clicked.connect( - functools.partial(self.omegaTweakCB, 1) - ) - hBoxSampleOrientationLayout.addWidget(setDC2CPButton) - hBoxSampleOrientationLayout.addWidget(omegaLabel) - hBoxSampleOrientationLayout.addWidget(self.sampleOmegaRBVLedit.getEntry()) - hBoxSampleOrientationLayout.addWidget(omegaSPLabel) - hBoxSampleOrientationLayout.addWidget(self.sampleOmegaMoveLedit.getEntry()) - spacerItem = QtWidgets.QSpacerItem( - 50, 1, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum - ) - hBoxSampleOrientationLayout.insertSpacing(5, 50) - hBoxSampleOrientationLayout.addWidget(omegaTweakNegButtonSuperFine) - hBoxSampleOrientationLayout.addWidget(omegaTweakNegButtonFine) - hBoxSampleOrientationLayout.addWidget(omegaTweakNegButton) - hBoxSampleOrientationLayout.addWidget(self.omegaTweakVal_ledit) - hBoxSampleOrientationLayout.addWidget(omegaTweakPosButton) - hBoxSampleOrientationLayout.addWidget(omegaTweakPosButtonFine) - hBoxSampleOrientationLayout.addWidget(omegaTweakPosButtonSuperFine) - hBoxSampleOrientationLayout.addStretch(1) - hBoxVidControlLayout = QtWidgets.QHBoxLayout() - lightLevelLabel = QtWidgets.QLabel("Light") - lightLevelLabel.setAlignment(QtCore.Qt.AlignRight | Qt.AlignVCenter) - sampleBrighterButton = QtWidgets.QPushButton("+") - sampleBrighterButton.setFixedWidth(30) - sampleBrighterButton.clicked.connect(self.lightUpCB) - sampleBrighterButton.setEnabled(False) # Disabling until PV is fixed - sampleDimmerButton = QtWidgets.QPushButton("-") - sampleDimmerButton.setFixedWidth(30) - sampleDimmerButton.clicked.connect(self.lightDimCB) - sampleDimmerButton.setEnabled(False) # Disabling until PV is fixed - focusLabel = QtWidgets.QLabel("Focus") - focusLabel.setAlignment(QtCore.Qt.AlignRight | Qt.AlignVCenter) - focusPlusButton = QtWidgets.QPushButton("+") - focusPlusButton.setFixedWidth(30) - focusPlusButton.clicked.connect(functools.partial(self.focusTweakCB, 5)) - focusMinusButton = QtWidgets.QPushButton("-") - focusMinusButton.setFixedWidth(30) - focusMinusButton.clicked.connect(functools.partial(self.focusTweakCB, -5)) - annealButton = QtWidgets.QPushButton("Anneal") - annealButton.clicked.connect(self.annealButtonCB) - annealTimeLabel = QtWidgets.QLabel("Time") - self.annealTime_ledit = QtWidgets.QLineEdit() - self.annealTime_ledit.setFixedWidth(40) - self.annealTime_ledit.setText("0.5") - magLevelLabel = QtWidgets.QLabel("Vid:") - snapshotButton = QtWidgets.QPushButton("SnapShot") - snapshotButton.clicked.connect(self.saveVidSnapshotButtonCB) - self.hideRastersCheckBox = QCheckBox("Hide\nRasters") - self.hideRastersCheckBox.setChecked(False) - self.hideRastersCheckBox.stateChanged.connect(self.hideRastersCB) - hBoxVidControlLayout.addWidget(self.zoom1Radio) - if ( - daq_utils.sampleCameraCount >= 2 - ): # Button naming assumes sequential camera ordering - hBoxVidControlLayout.addWidget(self.zoom2Radio) - if daq_utils.sampleCameraCount >= 3: - hBoxVidControlLayout.addWidget(self.zoom3Radio) - if daq_utils.sampleCameraCount >= 4: - hBoxVidControlLayout.addWidget(self.zoom4Radio) - else: - hBoxVidControlLayout.addLayout(hBoxZoomLevelLayout) - hBoxVidControlLayout.addWidget(focusLabel) - hBoxVidControlLayout.addWidget(focusPlusButton) - hBoxVidControlLayout.addWidget(focusMinusButton) - hBoxVidControlLayout.addWidget(lightLevelLabel) - hBoxVidControlLayout.addWidget(sampleBrighterButton) - hBoxVidControlLayout.addWidget(sampleDimmerButton) - hBoxVidControlLayout.addWidget(annealButton) - hBoxVidControlLayout.addWidget(annealTimeLabel) - hBoxVidControlLayout.addWidget(self.annealTime_ledit) - hBoxSampleAlignLayout = QtWidgets.QHBoxLayout() - centerLoopButton = QtWidgets.QPushButton("Center\nLoop") - centerLoopButton.clicked.connect(self.autoCenterLoopCB) - measureButton = QtWidgets.QPushButton("Measure") - measureButton.clicked.connect(self.measurePolyCB) - loopShapeButton = QtWidgets.QPushButton("Add Raster\nto Queue") - loopShapeButton.clicked.connect(self.drawInteractiveRasterCB) - runRastersButton = QtWidgets.QPushButton("Run\nRaster") - runRastersButton.clicked.connect(self.runRastersCB) - clearGraphicsButton = QtWidgets.QPushButton("Clear") - clearGraphicsButton.clicked.connect(self.eraseCB) - self.click3Button = QtWidgets.QPushButton("3-Click\nCenter") - self.click3Button.clicked.connect(self.center3LoopCB) - - self.threeClickCount = 0 - saveCenteringButton = QtWidgets.QPushButton("Save\nCenter") - saveCenteringButton.clicked.connect(self.saveCenterCB) - selectAllCenteringButton = QtWidgets.QPushButton("Select All\nCenterings") - selectAllCenteringButton.clicked.connect(self.selectAllCenterCB) - hBoxSampleAlignLayout.addWidget(centerLoopButton) - hBoxSampleAlignLayout.addWidget(clearGraphicsButton) - hBoxSampleAlignLayout.addWidget(saveCenteringButton) - hBoxSampleAlignLayout.addWidget(selectAllCenteringButton) - hBoxSampleAlignLayout.addWidget(self.click3Button) - hBoxSampleAlignLayout.addWidget(snapshotButton) - hBoxSampleAlignLayout.addWidget(self.hideRastersCheckBox) - self.click3Button.setMaximumSize(self.click3Button.sizeHint()) - hBoxRadioLayout100 = QtWidgets.QHBoxLayout() - vidActionLabel = QtWidgets.QLabel("Video Click Mode:") - self.vidActionRadioGroup = QtWidgets.QButtonGroup() - self.vidActionC2CRadio = QtWidgets.QRadioButton("C2C") - self.vidActionC2CRadio.setChecked(True) - self.vidActionC2CRadio.toggled.connect(self.vidActionToggledCB) - self.vidActionRadioGroup.addButton(self.vidActionC2CRadio) - self.vidActionDefineCenterRadio = QtWidgets.QRadioButton("Define Center") - self.vidActionDefineCenterRadio.setChecked(False) - self.vidActionDefineCenterRadio.setEnabled(False) - self.vidActionDefineCenterRadio.toggled.connect(self.vidActionToggledCB) - self.vidActionRadioGroup.addButton(self.vidActionDefineCenterRadio) - self.vidActionRasterExploreRadio = QtWidgets.QRadioButton("Raster Explore") - self.vidActionRasterExploreRadio.setChecked(False) - self.vidActionRasterExploreRadio.toggled.connect(self.vidActionToggledCB) - self.vidActionRadioGroup.addButton(self.vidActionRasterExploreRadio) - self.vidActionRasterSelectRadio = QtWidgets.QRadioButton("Raster Select") - self.vidActionRasterSelectRadio.setChecked(False) - self.vidActionRasterSelectRadio.toggled.connect(self.vidActionToggledCB) - self.vidActionRadioGroup.addButton(self.vidActionRasterSelectRadio) - self.vidActionRasterDefRadio = QtWidgets.QRadioButton("Define Raster") - self.vidActionRasterDefRadio.setChecked(False) - self.vidActionRasterDefRadio.setEnabled(False) - self.vidActionRasterDefRadio.toggled.connect(self.vidActionToggledCB) - self.vidActionRadioGroup.addButton(self.vidActionRasterDefRadio) - hBoxRadioLayout100.addWidget(vidActionLabel) - hBoxRadioLayout100.addWidget(self.vidActionC2CRadio) - hBoxRadioLayout100.addWidget(self.vidActionRasterExploreRadio) - hBoxRadioLayout100.addWidget(self.vidActionRasterDefRadio) - hBoxRadioLayout100.addWidget(self.vidActionDefineCenterRadio) - vBoxVidLayout.addLayout(hBoxSampleOrientationLayout) - vBoxVidLayout.addLayout(hBoxVidControlLayout) - vBoxVidLayout.addLayout(hBoxSampleAlignLayout) - vBoxVidLayout.addLayout(hBoxRadioLayout100) - self.VidFrame.setLayout(vBoxVidLayout) - splitter11.addWidget(self.mainSetupFrame) - self.colTabs = QtWidgets.QTabWidget() - self.energyFrame = QFrame() - vBoxEScanFull = QtWidgets.QVBoxLayout() - hBoxEScan = QtWidgets.QHBoxLayout() - vBoxEScan = QtWidgets.QVBoxLayout() - self.periodicTable = QPeriodicTable(butSize=20) - self.periodicTable.elementClicked("Se") - vBoxEScan.addWidget(self.periodicTable) - self.EScanDataPathGB = DataLocInfo(self) - vBoxEScan.addWidget(self.EScanDataPathGB) - hBoxEScanParams = QtWidgets.QHBoxLayout() - hBoxEScanButtons = QtWidgets.QHBoxLayout() - tempPlotButton = QtWidgets.QPushButton("Queue Requests") - tempPlotButton.clicked.connect(self.queueEnScanCB) - clearEnscanPlotButton = QtWidgets.QPushButton("Clear") - clearEnscanPlotButton.clicked.connect(self.clearEnScanPlotCB) - hBoxEScanButtons.addWidget(clearEnscanPlotButton) - hBoxEScanButtons.addWidget(tempPlotButton) - escanStepsLabel = QtWidgets.QLabel("Steps") - self.escan_steps_ledit = QtWidgets.QLineEdit() - self.escan_steps_ledit.setText("41") - escanStepsizeLabel = QtWidgets.QLabel("Stepsize (EVs)") - self.escan_stepsize_ledit = QtWidgets.QLineEdit() - self.escan_stepsize_ledit.setText("1") - hBoxEScanParams.addWidget(escanStepsLabel) - hBoxEScanParams.addWidget(self.escan_steps_ledit) - hBoxEScanParams.addWidget(escanStepsizeLabel) - hBoxEScanParams.addWidget(self.escan_stepsize_ledit) - hBoxChoochResults = QtWidgets.QHBoxLayout() - hBoxChoochResults2 = QtWidgets.QHBoxLayout() - choochResultsLabel = QtWidgets.QLabel("Chooch Results") - choochInflLabel = QtWidgets.QLabel("Infl") - self.choochInfl = QtWidgets.QLabel("") - self.choochInfl.setFixedWidth(70) - choochPeakLabel = QtWidgets.QLabel("Peak") - self.choochPeak = QtWidgets.QLabel("") - self.choochPeak.setFixedWidth(70) - choochInflFPrimeLabel = QtWidgets.QLabel("fPrimeInfl") - self.choochFPrimeInfl = QtWidgets.QLabel("") - self.choochFPrimeInfl.setFixedWidth(70) - choochInflF2PrimeLabel = QtWidgets.QLabel("f2PrimeInfl") - self.choochF2PrimeInfl = QtWidgets.QLabel("") - self.choochF2PrimeInfl.setFixedWidth(70) - choochPeakFPrimeLabel = QtWidgets.QLabel("fPrimePeak") - self.choochFPrimePeak = QtWidgets.QLabel("") - self.choochFPrimePeak.setFixedWidth(70) - choochPeakF2PrimeLabel = QtWidgets.QLabel("f2PrimePeak") - self.choochF2PrimePeak = QtWidgets.QLabel("") - self.choochF2PrimePeak.setFixedWidth(70) - hBoxChoochResults.addWidget(choochResultsLabel) - hBoxChoochResults.addWidget(choochInflLabel) - hBoxChoochResults.addWidget(self.choochInfl) - hBoxChoochResults.addWidget(choochPeakLabel) - hBoxChoochResults.addWidget(self.choochPeak) - hBoxChoochResults2.addWidget(choochInflFPrimeLabel) - hBoxChoochResults2.addWidget(self.choochFPrimeInfl) - hBoxChoochResults2.addWidget(choochInflF2PrimeLabel) - hBoxChoochResults2.addWidget(self.choochF2PrimeInfl) - hBoxChoochResults2.addWidget(choochPeakFPrimeLabel) - hBoxChoochResults2.addWidget(self.choochFPrimePeak) - hBoxChoochResults2.addWidget(choochPeakF2PrimeLabel) - hBoxChoochResults2.addWidget(self.choochF2PrimePeak) - vBoxEScan.addLayout(hBoxEScanParams) - vBoxEScan.addLayout(hBoxEScanButtons) - vBoxEScan.addLayout(hBoxChoochResults) - vBoxEScan.addLayout(hBoxChoochResults2) - hBoxEScan.addLayout(vBoxEScan) - verticalLine = QFrame() - verticalLine.setFrameStyle(QFrame.VLine) - self.EScanGraph = ScanWindow(self.energyFrame) - hBoxEScan.addWidget(verticalLine) - hBoxEScan.addWidget(self.EScanGraph) - vBoxEScanFull.addLayout(hBoxEScan) - self.choochGraph = ScanWindow( - self.energyFrame - ) # TODO should be another type? need to be able to add curves - vBoxEScanFull.addWidget(self.choochGraph) - self.energyFrame.setLayout(vBoxEScanFull) - splitter11.addWidget(self.VidFrame) - self.colTabs.addTab(splitter11, "Sample Control") - self.colTabs.addTab(self.energyFrame, "Energy Scan") - splitter1.addWidget(self.colTabs) - vBoxlayout.addWidget(splitter1) - self.lastFileLabel2 = QtWidgets.QLabel("File:") - self.lastFileLabel2.setFixedWidth(60) - if daq_utils.beamline == "amx": - self.lastFileRBV2 = QtEpicsPVLabel( - "XF:17IDB-ES:AMX{Det:Eig9M}cam1:FullFileName_RBV", self, 0 - ) - else: - self.lastFileRBV2 = QtEpicsPVLabel( - "XF:17IDC-ES:FMX{Det:Eig16M}cam1:FullFileName_RBV", self, 0 - ) - fileHBoxLayout = QtWidgets.QHBoxLayout() - fileHBoxLayout2 = QtWidgets.QHBoxLayout() - self.controlMasterCheckBox = QCheckBox("Control Master") - self.controlMasterCheckBox.stateChanged.connect(self.changeControlMasterCB) - self.controlMasterCheckBox.setChecked(False) - fileHBoxLayout.addWidget(self.controlMasterCheckBox) - self.statusLabel = QtEpicsPVLabel( - daq_utils.beamlineComm + "program_state", - self, - 150, - highlight_on_change=False, - ) - fileHBoxLayout.addWidget(self.statusLabel.getEntry()) - self.shutterStateLabel = QtWidgets.QLabel("Shutter State:") - governorMessageLabel = QtWidgets.QLabel("Governor Message:") - self.governorMessage = QtEpicsPVLabel( - daq_utils.pvLookupDict["governorMessage"], - self, - 140, - highlight_on_change=False, - ) - ringCurrentMessageLabel = QtWidgets.QLabel("Ring(mA):") - self.ringCurrentMessage = QtWidgets.QLabel(str(self.ringCurrent_pv.get())) - beamAvailable = self.beamAvailable_pv.get() - if beamAvailable: - self.beamAvailLabel = QtWidgets.QLabel("Beam Available") - self.beamAvailLabel.setStyleSheet("background-color: #99FF66;") - else: - self.beamAvailLabel = QtWidgets.QLabel("No Beam") - self.beamAvailLabel.setStyleSheet("background-color: red;") - sampleExposed = self.sampleExposed_pv.get() - if sampleExposed: - self.sampleExposedLabel = QtWidgets.QLabel("Sample Exposed") - self.sampleExposedLabel.setStyleSheet("background-color: red;") - else: - self.sampleExposedLabel = QtWidgets.QLabel("Sample Not Exposed") - self.sampleExposedLabel.setStyleSheet("background-color: #99FF66;") - gripperLabel = QtWidgets.QLabel("Gripper Temp:") - if daq_utils.beamline == "nyx": - self.gripperTempLabel = QtWidgets.QLabel("N/A") - else: - self.gripperTempLabel = QtWidgets.QLabel("%.1f" % self.gripTemp_pv.get()) - cryostreamLabel = QtWidgets.QLabel("Cryostream Temp:") - if getBlConfig(CRYOSTREAM_ONLINE): - self.cryostreamTempLabel = QtWidgets.QLabel( - str(self.cryostreamTemp_pv.get()) - ) - else: - self.cryostreamTempLabel = QtWidgets.QLabel("N/A") - - fileHBoxLayout.addWidget(gripperLabel) - fileHBoxLayout.addWidget(self.gripperTempLabel) - fileHBoxLayout.addWidget(cryostreamLabel) - fileHBoxLayout.addWidget(self.cryostreamTempLabel) - fileHBoxLayout.addWidget(ringCurrentMessageLabel) - fileHBoxLayout.addWidget(self.ringCurrentMessage) - fileHBoxLayout.addWidget(self.beamAvailLabel) - fileHBoxLayout.addWidget(self.sampleExposedLabel) - fileHBoxLayout.addWidget(governorMessageLabel) - fileHBoxLayout.addWidget(self.governorMessage.getEntry()) - fileHBoxLayout2.addWidget(self.lastFileLabel2) - fileHBoxLayout2.addWidget(self.lastFileRBV2.getEntry()) - vBoxlayout.addLayout(fileHBoxLayout) - vBoxlayout.addLayout(fileHBoxLayout2) - sampleTab.setLayout(vBoxlayout) - self.tabs.addTab(sampleTab, "Collect") - # 12/19 - uncomment this to expose the PyMCA XRF interface. It's not connected to anything. - self.zoomLevelToggledCB("Zoom1") - - if daq_utils.beamline == "nyx": # hiding unused GUI elements - self.protoRasterRadio.setVisible(False) - self.protoStandardRadio.setVisible(False) - self.protoVectorRadio.setVisible(False) - self.protoOtherRadio.setVisible(False) - self.autoProcessingCheckBox.setVisible(False) - self.fastEPCheckBox.setVisible(False) - self.dimpleCheckBox.setVisible(False) - self.centeringComboBox.setVisible(False) - annealButton.setVisible(False) - centerLoopButton.setVisible(False) - clearGraphicsButton.setVisible(False) - saveCenteringButton.setVisible(False) - selectAllCenteringButton.setVisible(False) - snapshotButton.setVisible(False) - annealTimeLabel.setVisible(False) - self.annealTime_ledit.setVisible(False) - self.vidActionDefineCenterRadio.setVisible(False) - self.hideRastersCheckBox.setEnabled(True) - self.vidActionC2CRadio.setEnabled(True) - self.vidActionRasterExploreRadio.setEnabled(True) - self.vidActionRasterDefRadio.setEnabled(True) - - - - #self.captureLowMag = cv2.VideoCapture(daq_utils.lowMagCamURL) - #self.captureLowMag.set(cv2.CAP_PROP_BUFFERSIZE, 1) - self.captureLowMag = daq_utils.lowMagCamURL - self.capture = self.captureLowMag - - #self.sampleCameraThread = VideoThread( - # parent=self, delay=SAMPLE_TIMER_DELAY, camera_object=self.capture - #) - self.sampleCameraThread = VideoThread( - parent=self, delay=HUTCH_TIMER_DELAY, url=daq_utils.highMagCamURL - ) - self.sampleCameraThread.frame_ready.connect( - lambda frame: self.updateCam(self.pixmap_item, frame) - ) - self.sampleCameraThread.start() - - - - self.hutchCornerCamThread = VideoThread( - parent=self, delay=HUTCH_TIMER_DELAY, url=getBlConfig("hutchCornerCamURL") - ) - self.hutchCornerCamThread.frame_ready.connect( - lambda frame: self.updateCam(self.pixmap_item_HutchCorner, frame) - ) - self.hutchCornerCamThread.start() - - self.hutchTopCamThread = VideoThread( - parent=self, delay=HUTCH_TIMER_DELAY, url=getBlConfig("hutchTopCamURL") - ) - self.hutchTopCamThread.frame_ready.connect( - lambda frame: self.updateCam(self.pixmap_item_HutchTop, frame) - ) - self.hutchTopCamThread.start() - serverCheckThread = ServerCheckThread( - parent=self, delay=SERVER_CHECK_DELAY) - serverCheckThread.visit_dir_changed.connect(QApplication.instance().quit) - serverCheckThread.start() - - def updateCam(self, pixmapItem: "QGraphicsPixmapItem", frame): - pixmapItem.setPixmap(frame) - - def albulaCheckCB(self, state): - if state != QtCore.Qt.Checked: - albulaUtils.albulaClose() - else: - albulaUtils.albulaOpen() # TODO there is no albulaOpen method! remove? - - def annealButtonCB(self): - try: - ftime = float(self.annealTime_ledit.text()) - if ftime >= 0.1 and ftime <= 5.0: - comm_s = "anneal(" + str(ftime) + ")" - logger.info(comm_s) - self.send_to_server(comm_s) - else: - self.popupServerMessage( - "Anneal time must be between 0.1 and 5.0 seconds." - ) - except: - pass - - def hideRastersCB(self, state): - if state == QtCore.Qt.Checked: - self.eraseRastersCB() - else: - self.refreshCollectionParams(self.selectedSampleRequest) - - def stillModeUserPushCB(self, state): - logger.info("still checkbox state " + str(state)) - if self.controlEnabled(): - if state: - self.stillMode_pv.put(1) - self.setGuiValues({"osc_range": "0.0"}) - else: - self.standardMode_pv.put(1) - else: - self.popupServerMessage("You don't have control") - if self.stillModeStatePV.get(): - self.stillModeCheckBox.setChecked(True) - else: - self.stillModeCheckBox.setChecked(False) - - def autoProcessingCheckCB(self, state): - if state == QtCore.Qt.Checked: - self.dimpleCheckBox.setEnabled(True) - self.xia2CheckBox.setEnabled(True) - else: - self.fastEPCheckBox.setEnabled(False) - self.dimpleCheckBox.setEnabled(False) - self.xia2CheckBox.setEnabled(False) - - def rasterGrainToggledCB(self, identifier): - if identifier == "Coarse" or identifier == "Fine" or identifier == "VFine": - cellSize = self.rasterStepDefs[identifier] - self.rasterStepEdit.setText(str(cellSize)) - self.beamWidth_ledit.setText(str(cellSize)) - self.beamHeight_ledit.setText(str(cellSize)) - - def vidActionToggledCB(self): - if len(self.rasterList) > 0: - if self.vidActionRasterSelectRadio.isChecked(): - for i in range(len(self.rasterList)): - if self.rasterList[i] != None: - self.rasterList[i]["graphicsItem"].setFlag( - QtWidgets.QGraphicsItem.ItemIsSelectable, True - ) - else: - for i in range(len(self.rasterList)): - if self.rasterList[i] != None: - self.rasterList[i]["graphicsItem"].setFlag( - QtWidgets.QGraphicsItem.ItemIsMovable, False - ) - self.rasterList[i]["graphicsItem"].setFlag( - QtWidgets.QGraphicsItem.ItemIsSelectable, False - ) - if self.vidActionRasterDefRadio.isChecked(): - self.click_positions = [] - self.showProtParams() - if self.vidActionC2CRadio.isChecked(): - self.click_positions = [] - if ( - self.protoComboBox.findText(str("raster")) - == self.protoComboBox.currentIndex() - or self.protoComboBox.findText(str("stepRaster")) - == self.protoComboBox.currentIndex() - ): - self.protoComboBox.setCurrentIndex( - self.protoComboBox.findText(str("standard")) - ) - self.protoComboActivatedCB("standard") - self.showProtParams() - - def adjustGraphics4ZoomChange(self, fov): - imageScaleMicrons = ( - int(round(self.imageScaleLineLen * (fov["x"] / daq_utils.screenPixX))) - * daq_utils.unitScaling - ) - self.imageScaleText.setText(str(imageScaleMicrons) + " microns") - if self.rasterList != []: - saveRasterList = self.rasterList - self.eraseDisplayCB() - for i in range(len(saveRasterList)): - if saveRasterList[i] == None: - self.rasterList.append(None) - else: - rasterXPixels = float(saveRasterList[i]["graphicsItem"].x()) - rasterYPixels = float(saveRasterList[i]["graphicsItem"].y()) - self.rasterXmicrons = rasterXPixels * ( - fov["x"] / daq_utils.screenPixX - ) - self.rasterYmicrons = rasterYPixels * ( - fov["y"] / daq_utils.screenPixY - ) - if not self.hideRastersCheckBox.isChecked(): - self.drawPolyRaster( - db_lib.getRequestByID(saveRasterList[i]["uid"]), - saveRasterList[i]["coords"]["x"], - saveRasterList[i]["coords"]["y"], - saveRasterList[i]["coords"]["z"], - ) - self.fillPolyRaster( - db_lib.getRequestByID(saveRasterList[i]["uid"]) - ) - self.processSampMove(self.gon.x.val(), "x") - self.processSampMove(self.gon.y.val(), "y") - self.processSampMove(self.gon.z.val(), "z") - if self.vectorStart != None: - self.processSampMove(self.gon.x.val(), "x") - self.processSampMove(self.gon.y.val(), "y") - self.processSampMove(self.gon.z.val(), "z") - if self.centeringMarksList != []: - self.processSampMove(self.gon.x.val(), "x") - self.processSampMove(self.gon.y.val(), "y") - self.processSampMove(self.gon.z.val(), "z") - - def flushBuffer(self, vidStream): - if vidStream == None: - return - for i in range(0, 1000): - stime = time.time() - vidStream.grab() - etime = time.time() - commTime = etime - stime - if commTime > 0.01: - return - - def zoomLevelComboActivatedCB(self, identifier): - self.camera.zoom.put(identifier) - self.centerMarker.setPos(self.getMD2BeamCenterX()-self.centerMarkerCharOffsetX, self.getMD2BeamCenterY()-self.centerMarkerCharOffsetY) - #self.flushBuffer(self.capture) - #self.capture.release() - #self.capture = cv2.VideoCapture(daq_utils.lowMagZoomCamURL) - - def zoomLevelToggledCB(self, identifier): - fov = {} - zoomedCursorX = self.getMD2BeamCenterX() - self.centerMarkerCharOffsetX - zoomedCursorY = self.getMD2BeamCenterY() - self.centerMarkerCharOffsetY - if self.zoom2Radio.isChecked(): - self.flushBuffer(self.captureLowMagZoom) - self.capture = self.captureLowMagZoom - fov["x"] = daq_utils.lowMagFOVx / 2.0 - fov["y"] = daq_utils.lowMagFOVy / 2.0 - unzoomedCursorX = self.lowMagCursorX_pv.get() - self.centerMarkerCharOffsetX - unzoomedCursorY = self.lowMagCursorY_pv.get() - self.centerMarkerCharOffsetY - if unzoomedCursorX * 2.0 < self.getMD2BeamCenterX(): - zoomedCursorX = unzoomedCursorX * 2.0 - if unzoomedCursorY * 2.0 < self.getMD2BeamCenterY(): - zoomedCursorY = unzoomedCursorY * 2.0 - if ( - unzoomedCursorX - self.getMD2BeamCenterX() - > self.getMD2BeamCenterX() / 2 - ): - zoomedCursorX = (unzoomedCursorX * 2.0) - daq_utils.screenPixX - if ( - unzoomedCursorY - self.getMD2BeamCenterY() - > self.getMD2BeamCenterY() / 2 - ): - zoomedCursorY = (unzoomedCursorY * 2.0) - daq_utils.screenPixY - self.centerMarker.setPos(zoomedCursorX, zoomedCursorY) - self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) - self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) - self.beamSizeOverlay.setRect( - self.overlayPosOffsetX - + self.centerMarker.x() - - (self.beamSizeXPixels / 2), - self.overlayPosOffsetY - + self.centerMarker.y() - - (self.beamSizeYPixels / 2), - self.beamSizeXPixels, - self.beamSizeYPixels, - ) - elif self.zoom1Radio.isChecked(): - self.flushBuffer(self.captureLowMag) - self.capture = self.captureLowMag - fov["x"] = daq_utils.lowMagFOVx - fov["y"] = daq_utils.lowMagFOVy - self.centerMarker.setPos( - self.lowMagCursorX_pv.get() - self.centerMarkerCharOffsetX, - self.lowMagCursorY_pv.get() - self.centerMarkerCharOffsetY, - ) - self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) - self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) - self.beamSizeOverlay.setRect( - self.overlayPosOffsetX - + self.centerMarker.x() - - (self.beamSizeXPixels / 2), - self.overlayPosOffsetY - + self.centerMarker.y() - - (self.beamSizeYPixels / 2), - self.beamSizeXPixels, - self.beamSizeYPixels, - ) - elif self.zoom4Radio.isChecked(): - self.flushBuffer(self.captureHighMagZoom) - self.capture = self.captureHighMagZoom - fov["x"] = daq_utils.highMagFOVx / 2.0 - fov["y"] = daq_utils.highMagFOVy / 2.0 - unzoomedCursorX = ( - self.highMagCursorX_pv.get() - self.centerMarkerCharOffsetX - ) - unzoomedCursorY = ( - self.highMagCursorY_pv.get() - self.centerMarkerCharOffsetY - ) - if unzoomedCursorX * 2.0 < self.getMD2BeamCenterX(): - zoomedCursorX = unzoomedCursorX * 2.0 - if unzoomedCursorY * 2.0 < self.getMD2BeamCenterY(): - zoomedCursorY = unzoomedCursorY * 2.0 - if ( - unzoomedCursorX - self.getMD2BeamCenterX() - > self.getMD2BeamCenterX() / 2 - ): - zoomedCursorX = (unzoomedCursorX * 2.0) - daq_utils.screenPixX - if ( - unzoomedCursorY - self.getMD2BeamCenterY() - > self.getMD2BeamCenterY() / 2 - ): - zoomedCursorY = (unzoomedCursorY * 2.0) - daq_utils.screenPixY - self.centerMarker.setPos(zoomedCursorX, zoomedCursorY) - self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) - self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) - self.beamSizeOverlay.setRect( - self.overlayPosOffsetX - + self.centerMarker.x() - - (self.beamSizeXPixels / 2), - self.overlayPosOffsetY - + self.centerMarker.y() - - (self.beamSizeYPixels / 2), - self.beamSizeXPixels, - self.beamSizeYPixels, - ) - elif self.zoom3Radio.isChecked(): - self.flushBuffer(self.captureHighMag) - self.capture = self.captureHighMag - fov["x"] = daq_utils.highMagFOVx - fov["y"] = daq_utils.highMagFOVy - self.centerMarker.setPos( - self.highMagCursorX_pv.get() - self.centerMarkerCharOffsetX, - self.highMagCursorY_pv.get() - self.centerMarkerCharOffsetY, - ) - self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) - self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) - self.beamSizeOverlay.setRect( - self.overlayPosOffsetX - + self.centerMarker.x() - - (self.beamSizeXPixels / 2), - self.overlayPosOffsetY - + self.centerMarker.y() - - (self.beamSizeYPixels / 2), - self.beamSizeXPixels, - self.beamSizeYPixels, - ) - self.adjustGraphics4ZoomChange(fov) - self.sampleZoomChangeSignal.emit(self.capture) - - def saveVidSnapshotButtonCB(self): - comment, useOlog, ok = SnapCommentDialog.getComment() - if ok: - self.saveVidSnapshotCB(comment, useOlog) - - def saveVidSnapshotCB( - self, comment="", useOlog=False, reqID=None, rasterHeatJpeg=None - ): - if not os.path.exists("snapshots"): - os.system("mkdir snapshots") - width = 640 - height = 512 - targetrect = QRectF(0, 0, width, height) - sourcerect = QRectF(0, 0, width, height) - pix = QtGui.QPixmap(width, height) - painter = QtGui.QPainter(pix) - self.scene.render(painter, targetrect, sourcerect) - painter.end() - now = time.time() - if rasterHeatJpeg == None: - if reqID != None: - filePrefix = db_lib.getRequestByID(reqID)["request_obj"]["file_prefix"] - imagePath = ( - f"{getBlConfig('visitDirectory')}/snapshots/{filePrefix}{int(now)}.jpg" - ) - else: - if self.dataPathGB.prefix_ledit.text() != "": - imagePath = ( - f"{getBlConfig('visitDirectory')}/snapshots/{self.dataPathGB.prefix_ledit.text()}{int(now)}.jpg" - ) - else: - imagePath = ( - f"{getBlConfig('visitDirectory')}/snapshots/capture{int(now)}.jpg" - ) - else: - imagePath = rasterHeatJpeg - logger.info("saving " + imagePath) - pix.save(imagePath, "JPG") - if useOlog: - lsdcOlog.toOlogPicture(imagePath, str(comment)) - resultObj = {} - imgRef = imagePath # for now, just the path, might want to use filestore later, if they really do facilitate moving files - resultObj["data"] = imgRef - resultObj["comment"] = str(comment) - if ( - reqID != None - ): # assuming raster here, but will probably need to check the type - db_lib.addResultforRequest( - "rasterJpeg", - reqID, - owner=daq_utils.owner, - result_obj=resultObj, - proposalID=daq_utils.getProposalID(), - beamline=daq_utils.beamline, - ) - else: # the user pushed the snapshot button on the gui - mountedSampleID = self.mountedPin_pv.get() - if mountedSampleID != "": - db_lib.addResulttoSample( - "snapshotResult", - mountedSampleID, - owner=daq_utils.owner, - result_obj=resultObj, - proposalID=daq_utils.getProposalID(), - beamline=daq_utils.beamline, - ) - else: # beamline result, no sample mounted - db_lib.addResulttoBL( - "snapshotResult", - daq_utils.beamline, - owner=daq_utils.owner, - result_obj=resultObj, - proposalID=daq_utils.getProposalID(), - ) - - def changeControlMasterCB( - self, state, processID=os.getpid() - ): # when someone touches checkbox, either through interaction or code - logger.info("change control master") - logger.info(processID) - currentMaster = self.controlMaster_pv.get() - if currentMaster < 0: - self.controlMaster_pv.put( - currentMaster - ) # this makes sure if things are locked, and someone tries to get control, their checkbox will uncheck itself - self.popupServerMessage("Control is locked by staff. Please stand by.") - return - if state == QtCore.Qt.Checked: - self.controlMaster_pv.put(processID) - if ( - len(self.osc_range_ledit.text()) == 0 - or abs(float(self.osc_range_ledit.text())) > 0 - ): - self.standardMode_pv.put(1) - elif float(self.osc_range_ledit.text()) == 0: - self.stillMode_pv.put(1) - else: - self.userScreenDialog.hide() - if self.staffScreenDialog != None: - self.staffScreenDialog.hide() - - def calculateNewYCoordPos(self, startYX, startYY): - startY_pixels = 0 - zMotRBV = self.motPos["y"] - yMotRBV = self.motPos["z"] - if self.scannerType == "PI": - fineYRBV = self.motPos["fineY"] - fineZRBV = self.motPos["fineZ"] - deltaYX = startYX - zMotRBV - fineZRBV - deltaYY = startYY - yMotRBV - fineYRBV - else: - deltaYX = startYX - zMotRBV - deltaYY = startYY - yMotRBV - omegaRad = math.radians(self.motPos["omega"]) - newYY = ( - float(startY_pixels - (self.screenYmicrons2pixels(deltaYY))) - ) * math.sin(omegaRad) - newYX = ( - float(startY_pixels - (self.screenYmicrons2pixels(deltaYX))) - ) * math.cos(omegaRad) - newY = newYX + newYY - return newY - - def processROIChange(self, posRBV, ID): - pass - - def processLowMagCursorChange(self, posRBV, ID): - zoomedCursorX = self.getMD2BeamCenterX() - self.centerMarkerCharOffsetX - zoomedCursorY = self.getMD2BeamCenterY() - self.centerMarkerCharOffsetY - if self.zoom2Radio.isChecked(): # lowmagzoom - unzoomedCursorX = self.lowMagCursorX_pv.get() - self.centerMarkerCharOffsetX - unzoomedCursorY = self.lowMagCursorY_pv.get() - self.centerMarkerCharOffsetY - if unzoomedCursorX * 2.0 < self.getMD2BeamCenterX(): - zoomedCursorX = unzoomedCursorX * 2.0 - if unzoomedCursorY * 2.0 < self.getMD2BeamCenterY(): - zoomedCursorY = unzoomedCursorY * 2.0 - if ( - unzoomedCursorX - self.getMD2BeamCenterX() - > self.getMD2BeamCenterX() / 2 - ): - zoomedCursorX = (unzoomedCursorX * 2.0) - daq_utils.screenPixX - if ( - unzoomedCursorY - self.getMD2BeamCenterY() - > self.getMD2BeamCenterY() / 2 - ): - zoomedCursorY = (unzoomedCursorY * 2.0) - daq_utils.screenPixY - self.centerMarker.setPos(zoomedCursorX, zoomedCursorY) - self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) - self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) - self.beamSizeOverlay.setRect( - self.overlayPosOffsetX - + self.centerMarker.x() - - (self.beamSizeXPixels / 2), - self.overlayPosOffsetY - + self.centerMarker.y() - - (self.beamSizeYPixels / 2), - self.beamSizeXPixels, - self.beamSizeYPixels, - ) - else: - self.centerMarker.setPos( - self.lowMagCursorX_pv.get() - self.centerMarkerCharOffsetX, - self.lowMagCursorY_pv.get() - self.centerMarkerCharOffsetY, - ) - self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) - self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) - self.beamSizeOverlay.setRect( - self.overlayPosOffsetX - + self.centerMarker.x() - - (self.beamSizeXPixels / 2), - self.overlayPosOffsetY - + self.centerMarker.y() - - (self.beamSizeYPixels / 2), - self.beamSizeXPixels, - self.beamSizeYPixels, - ) - - def processHighMagCursorChange(self, posRBV, ID): - zoomedCursorX = self.getMD2BeamCenterX() - self.centerMarkerCharOffsetX - zoomedCursorY = self.getMD2BeamCenterY() - self.centerMarkerCharOffsetY - if self.zoom4Radio.isChecked(): # highmagzoom - unzoomedCursorX = ( - self.highMagCursorX_pv.get() - self.centerMarkerCharOffsetX - ) - unzoomedCursorY = ( - self.highMagCursorY_pv.get() - self.centerMarkerCharOffsetY - ) - if unzoomedCursorX * 2.0 < self.getMD2BeamCenterX(): - zoomedCursorX = unzoomedCursorX * 2.0 - if unzoomedCursorY * 2.0 < self.getMD2BeamCenterY(): - zoomedCursorY = unzoomedCursorY * 2.0 - if ( - unzoomedCursorX - self.getMD2BeamCenterX() - > self.getMD2BeamCenterX() / 2 - ): - zoomedCursorX = (unzoomedCursorX * 2.0) - daq_utils.screenPixX - if ( - unzoomedCursorY - self.getMD2BeamCenterY() - > self.getMD2BeamCenterY() / 2 - ): - zoomedCursorY = (unzoomedCursorY * 2.0) - daq_utils.screenPixY - self.centerMarker.setPos(zoomedCursorX, zoomedCursorY) - self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) - self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) - self.beamSizeOverlay.setRect( - self.overlayPosOffsetX - + self.centerMarker.x() - - (self.beamSizeXPixels / 2), - self.overlayPosOffsetY - + self.centerMarker.y() - - (self.beamSizeYPixels / 2), - self.beamSizeXPixels, - self.beamSizeYPixels, - ) - else: - self.centerMarker.setPos( - self.highMagCursorX_pv.get() - self.centerMarkerCharOffsetX, - self.highMagCursorY_pv.get() - self.centerMarkerCharOffsetY, - ) - self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) - self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) - self.beamSizeOverlay.setRect( - self.overlayPosOffsetX - + self.centerMarker.x() - - (self.beamSizeXPixels / 2), - self.overlayPosOffsetY - + self.centerMarker.y() - - (self.beamSizeYPixels / 2), - self.beamSizeXPixels, - self.beamSizeYPixels, - ) - - def processSampMove(self, posRBV, motID): - # print "new " + motID + " pos=" + str(posRBV) - self.motPos[motID] = posRBV - if self.centeringMarksList: - for mark in self.centeringMarksList: - if mark is None: - continue - centerMarkerOffsetX = mark["centerCursorX"] - self.centerMarker.x() - centerMarkerOffsetY = mark["centerCursorY"] - self.centerMarker.y() - if motID == "x": - startX = mark["sampCoords"]["x"] - delta = startX - posRBV - newX = float(self.screenXmicrons2pixels(delta)) - mark["graphicsItem"].setPos( - newX - centerMarkerOffsetX, mark["graphicsItem"].y() - ) - if motID == "y" or motID == "z" or motID == "omega": - startYY = mark["sampCoords"]["z"] - startYX = mark["sampCoords"]["y"] - newY = self.calculateNewYCoordPos(startYX, startYY) - mark["graphicsItem"].setPos( - mark["graphicsItem"].x(), newY - centerMarkerOffsetY - ) - if self.rasterList: - for raster in self.rasterList: - if raster is None: - continue - startX = raster["coords"]["x"] - startYY = raster["coords"]["z"] - startYX = raster["coords"]["y"] - if motID == "x": - delta = startX - posRBV - newX = float(self.screenXmicrons2pixels(delta)) - raster["graphicsItem"].setPos(newX, raster["graphicsItem"].y()) - if motID == "y" or motID == "z": - newY = self.calculateNewYCoordPos(startYX, startYY) - raster["graphicsItem"].setPos(raster["graphicsItem"].x(), newY) - if motID == "fineX": - delta = startX - posRBV - self.motPos["x"] - newX = float(self.screenXmicrons2pixels(delta)) - raster["graphicsItem"].setPos(newX, raster["graphicsItem"].y()) - if motID == "fineY" or motID == "fineZ": - newY = self.calculateNewYCoordPos(startYX, startYY) - raster["graphicsItem"].setPos(raster["graphicsItem"].x(), newY) - if motID == "omega": - if abs(posRBV - raster["coords"]["omega"]) % 360.0 > 5.0: - raster["graphicsItem"].setVisible(False) - else: - raster["graphicsItem"].setVisible(True) - newY = self.calculateNewYCoordPos(startYX, startYY) - raster["graphicsItem"].setPos(raster["graphicsItem"].x(), newY) - - self.vectorStart = self.updatePoint(self.vectorStart, posRBV, motID) - self.vectorEnd = self.updatePoint(self.vectorEnd, posRBV, motID) - if self.vectorStart != None and self.vectorEnd != None: - self.vecLine.setLine( - self.vectorStart["graphicsitem"].x() - + self.vectorStart["centerCursorX"] - + self.centerMarkerCharOffsetX, - self.vectorStart["graphicsitem"].y() - + self.vectorStart["centerCursorY"] - + self.centerMarkerCharOffsetY, - self.vectorEnd["graphicsitem"].x() - + self.vectorStart["centerCursorX"] - + self.centerMarkerCharOffsetX, - self.vectorEnd["graphicsitem"].y() - + self.vectorStart["centerCursorY"] - + self.centerMarkerCharOffsetY, - ) - - def updatePoint(self, point, posRBV, motID): - """Updates a point on the screen - - Updates the position of a point (e.g. self.vectorStart) drawn on the screen based on - which motor was moved (motID) using gonio position (posRBV) - """ - if point is None: - return point - centerMarkerOffsetX = point["centerCursorX"] - self.centerMarker.x() - centerMarkerOffsetY = point["centerCursorY"] - self.centerMarker.y() - startYY = point["coords"]["z"] - startYX = point["coords"]["y"] - startX = point["coords"]["x"] - - if motID == "omega": - newY = self.calculateNewYCoordPos(startYX, startYY) - point["graphicsitem"].setPos( - point["graphicsitem"].x(), newY - centerMarkerOffsetY - ) - if motID == "x": - delta = startX - posRBV - newX = float(self.screenXmicrons2pixels(delta)) - point["graphicsitem"].setPos( - newX - centerMarkerOffsetX, point["graphicsitem"].y() - ) - if motID == "y" or motID == "z": - newY = self.calculateNewYCoordPos(startYX, startYY) - point["graphicsitem"].setPos( - point["graphicsitem"].x(), newY - centerMarkerOffsetY - ) - return point - - def queueEnScanCB(self): - self.protoComboBox.setCurrentIndex(self.protoComboBox.findText(str("eScan"))) - self.addRequestsToAllSelectedCB() - self.treeChanged_pv.put(1) - - def clearEnScanPlotCB(self): - self.EScanGraph.removeCurves() # get list of all curves to provide to method? - self.choochGraph.removeCurves() - - def displayXrecRaster(self, xrecRasterFlag): - self.xrecRasterFlag_pv.put("0") - if xrecRasterFlag == "100": - for i in range(len(self.rasterList)): - if self.rasterList[i] != None: - self.scene.removeItem(self.rasterList[i]["graphicsItem"]) - else: - logger.info("xrecrasterflag = %s" % xrecRasterFlag) - try: - rasterReq = db_lib.getRequestByID(xrecRasterFlag) - except IndexError: - logger.error("bad xrecRasterFlag: %s" % xrecRasterFlag) - return - rasterDef = rasterReq["request_obj"]["rasterDef"] - if rasterDef["status"] == RasterStatus.DRAWN.value: - self.drawPolyRaster(rasterReq) - elif rasterDef["status"] == RasterStatus.READY_FOR_FILL.value: - self.fillPolyRaster( - rasterReq, waitTime=getBlConfig(RASTER_GUI_XREC_FILL_DELAY) - ) - logger.info("polyraster filled by displayXrecRaster") - elif rasterDef["status"] == RasterStatus.READY_FOR_SNAPSHOT.value: - if self.controlEnabled(): - self.takeRasterSnapshot(rasterReq) - logger.info("raster snapshot taken") - self.vidActionRasterExploreRadio.setChecked(True) - self.selectedSampleID = rasterReq["sample"] - self.treeChanged_pv.put(1) # not sure about this - elif rasterDef["status"] == RasterStatus.READY_FOR_REPROCESS.value: - self.fillPolyRaster(rasterReq) - logger.info("reprocessed polyraster filled by displayXrecraster") - if self.controlEnabled(): - self.takeRasterSnapshot(rasterReq) - logger.info("reprocessed raster snapshot taken") - self.vidActionRasterExploreRadio.setChecked(True) - self.selectedSampleID = rasterReq["sample"] - self.treeChanged_pv.put(1) # not sure about this - else: - pass - - def processMountedPin(self, mountedPinPos): - self.eraseCB() - self.treeChanged_pv.put(1) - - def processFastShutter(self, shutterVal): - if round(shutterVal) == round(self.fastShutterOpenPos_pv.get()): - self.shutterStateLabel.setText("Shutter State:Open") - self.shutterStateLabel.setStyleSheet("background-color: red;") - else: - self.shutterStateLabel.setText("Shutter State:Closed") - self.shutterStateLabel.setStyleSheet("background-color: #99FF66;") - - def processGripTemp(self, gripVal): - self.gripperTempLabel.setText("%.1f" % gripVal) - if int(gripVal) > -170: - self.gripperTempLabel.setStyleSheet("background-color: red;") - else: - self.gripperTempLabel.setStyleSheet("background-color: #99FF66;") - - def processCryostreamTemp(self, cryostreamVal): - self.cryostreamTempLabel.setText(str(cryostreamVal)) - - def processRingCurrent(self, ringCurrentVal): - self.ringCurrentMessage.setText(str(int(ringCurrentVal))) - if int(ringCurrentVal) < 390: - self.ringCurrentMessage.setStyleSheet("background-color: red;") - else: - self.ringCurrentMessage.setStyleSheet("background-color: #99FF66;") - - def processBeamAvailable(self, beamAvailVal): - if int(beamAvailVal) == 1: - self.beamAvailLabel.setText("Beam Available") - self.beamAvailLabel.setStyleSheet("background-color: #99FF66;") - else: - self.beamAvailLabel.setText("No Beam") - self.beamAvailLabel.setStyleSheet("background-color: red;") - - def processSampleExposed(self, sampleExposedVal): - if int(sampleExposedVal) == 1: - self.sampleExposedLabel.setText("Sample Exposed") - self.sampleExposedLabel.setStyleSheet("background-color: red;") - else: - self.sampleExposedLabel.setText("Sample Not Exposed") - self.sampleExposedLabel.setStyleSheet("background-color: #99FF66;") - - def processBeamSize(self, beamSizeFlag): - self.beamsizeComboBox.setCurrentIndex(beamSizeFlag) - - def processEnergyChange(self, energyVal): - if daq_utils.beamline != "amx": - if energyVal < 9000: - self.beamsizeComboBox.setEnabled(False) - else: - self.beamsizeComboBox.setEnabled(True) - - def processControlMaster(self, controlPID): - logger.info("in callback controlPID = " + str(controlPID)) - if abs(int(controlPID)) == self.processID: - self.controlMasterCheckBox.setChecked(True) - else: - self.controlMasterCheckBox.setChecked(False) - - def processZebraArmState(self, state): - if int(state): - self.userScreenDialog.zebraArmCheckBox.setChecked(True) - else: - self.userScreenDialog.zebraArmCheckBox.setChecked(False) - - def processGovRobotSeReach(self, state): - if int(state): - self.userScreenDialog.SEbutton.setEnabled(True) - else: - self.userScreenDialog.SEbutton.setEnabled(False) - - def processGovRobotSaReach(self, state): - if int(state): - self.userScreenDialog.SAbutton.setEnabled(True) - else: - self.userScreenDialog.SAbutton.setEnabled(False) - - def processGovRobotDaReach(self, state): - if int(state): - self.userScreenDialog.DAbutton.setEnabled(True) - else: - self.userScreenDialog.DAbutton.setEnabled(False) - - def processGovRobotBlReach(self, state): - if int(state): - self.userScreenDialog.BLbutton.setEnabled(True) - else: - self.userScreenDialog.BLbutton.setEnabled(False) - - def processDetMessage(self, state): - self.userScreenDialog.detMessage_ledit.setText(str(state)) - - def processSampleFlux(self, state): - self.userScreenDialog.sampleFluxLabel.setText("%E" % state) - - def processZebraPulseState(self, state): - if int(state): - self.userScreenDialog.zebraPulseCheckBox.setChecked(True) - else: - self.userScreenDialog.zebraPulseCheckBox.setChecked(False) - - def processStillModeState(self, state): - if int(state): - self.stillModeCheckBox.setChecked(True) - else: - self.stillModeCheckBox.setChecked(False) - - def processZebraDownloadState(self, state): - if int(state): - self.userScreenDialog.zebraDownloadCheckBox.setChecked(True) - else: - self.userScreenDialog.zebraDownloadCheckBox.setChecked(False) - - def processZebraSentTriggerState(self, state): - if int(state): - self.userScreenDialog.zebraSentTriggerCheckBox.setChecked(True) - else: - self.userScreenDialog.zebraSentTriggerCheckBox.setChecked(False) - - def processZebraReturnedTriggerState(self, state): - if int(state): - self.userScreenDialog.zebraReturnedTriggerCheckBox.setChecked(True) - else: - self.userScreenDialog.zebraReturnedTriggerCheckBox.setChecked(False) - - def processControlMasterNew(self, controlPID): - logger.info("in callback controlPID = " + str(controlPID)) - if abs(int(controlPID)) != self.processID: - self.controlMasterCheckBox.setChecked(False) - - def processChoochResult(self, choochResultFlag): - if choochResultFlag == "0": - return - try: - choochResult = db_lib.getResult(choochResultFlag) - except IndexError: # if no result, just return as if no result - logger.info( - f"Exception while retrieving result for chooch result so not plotting but continuing GUI: {choochResultFlag}" - ) - return - choochResultObj = choochResult["result_obj"] - graph_x = choochResultObj["choochInXAxis"] - graph_y = choochResultObj["choochInYAxis"] - self.EScanGraph.name = "Chooch PLot" - try: - self.EScanGraph.addCurve(graph_x, graph_y, "Raw counts vs. energy") - self.EScanGraph.replot() - except TypeError as e: - logger.error( - "Problems with data type going into energy scan plot: %s" % (e) - ) - chooch_graph_x = choochResultObj["choochOutXAxis"] - chooch_graph_y1 = choochResultObj["choochOutY1Axis"] - chooch_graph_y2 = choochResultObj["choochOutY2Axis"] - self.choochGraph.name = "Chooch PLot" - try: - self.choochGraph.addCurve(chooch_graph_x, chooch_graph_y1, legend="spline") - self.choochGraph.addCurve(chooch_graph_x, chooch_graph_y2, legend="fp") - self.choochGraph.replot() - self.choochInfl.setText(str(choochResultObj["infl"])) - self.choochPeak.setText(str(choochResultObj["peak"])) - self.choochFPrimeInfl.setText(str(choochResultObj["fprime_infl"])) - self.choochFPrimePeak.setText(str(choochResultObj["fprime_peak"])) - self.choochF2PrimeInfl.setText(str(choochResultObj["f2prime_infl"])) - self.choochF2PrimePeak.setText(str(choochResultObj["f2prime_peak"])) - self.choochResultFlag_pv.put("0") - self.protoComboBox.setCurrentIndex( - self.protoComboBox.findText(str("standard")) - ) - self.protoComboActivatedCB("standard") - except TypeError as e: - logger.error( - "Chooch plotting failed - check whether scan had a strong signal or not: %s" - % (e) - ) - - # seems like we should be able to do an aggregate query to mongo for max/min :( - def getMaxPriority(self): - orderedRequests = db_lib.getOrderedRequestList(daq_utils.beamline) - priorityMax = 0 - for i in range(len(orderedRequests)): - if orderedRequests[i]["priority"] > priorityMax: - priorityMax = orderedRequests[i]["priority"] - return priorityMax - - def getMinPriority(self): - orderedRequests = db_lib.getOrderedRequestList(daq_utils.beamline) - priorityMin = 10000000 - for i in range(len(orderedRequests)): - if (orderedRequests[i]["priority"] < priorityMin) and orderedRequests[i][ - "priority" - ] > 0: - priorityMin = orderedRequests[i]["priority"] - return priorityMin - - def showProtParams(self): - protocol = str(self.protoComboBox.currentText()) - self.rasterParamsFrame.hide() - self.characterizeParamsFrame.hide() - self.processingOptionsFrame.hide() - self.multiColParamsFrame.hide() - self.osc_start_ledit.setEnabled(True) - self.osc_end_ledit.setEnabled(True) - if protocol == "raster" or protocol == "rasterScreen": - self.rasterParamsFrame.show() - self.osc_start_ledit.setEnabled(False) - self.osc_end_ledit.setEnabled(False) - - elif protocol == "stepRaster": - self.rasterParamsFrame.show() - self.processingOptionsFrame.show() - elif protocol == "multiCol" or protocol == "multiColQ": - self.rasterParamsFrame.show() - self.osc_start_ledit.setEnabled(False) - self.osc_end_ledit.setEnabled(False) - self.multiColParamsFrame.show() - elif protocol == "vector" or protocol == "stepVector": - self.vectorParamsFrame.show() - self.processingOptionsFrame.show() - elif protocol == "characterize" or protocol == "ednaCol": - self.characterizeParamsFrame.show() - self.processingOptionsFrame.show() - elif protocol == "standard" or protocol == "burn": - self.processingOptionsFrame.show() - else: - pass - - def rasterStepChanged(self, text): - self.beamWidth_ledit.setText(text) - self.beamHeight_ledit.setText(text) - - def updateVectorLengthAndSpeed(self): - x_vec_end = self.vectorEnd["coords"]["x"] - y_vec_end = self.vectorEnd["coords"]["y"] - z_vec_end = self.vectorEnd["coords"]["z"] - x_vec_start = self.vectorStart["coords"]["x"] - y_vec_start = self.vectorStart["coords"]["y"] - z_vec_start = self.vectorStart["coords"]["z"] - x_vec = x_vec_end - x_vec_start - y_vec = y_vec_end - y_vec_start - z_vec = z_vec_end - z_vec_start - trans_total = math.sqrt(x_vec**2 + y_vec**2 + z_vec**2) - if daq_utils.beamline == "nyx": - trans_total *= 1000 - self.vecLenLabelOutput.setText(str(int(trans_total))) - totalExpTime = ( - float(self.osc_end_ledit.text()) / float(self.osc_range_ledit.text()) - ) * float( - self.exp_time_ledit.text() - ) # (range/inc)*exptime - speed = trans_total / totalExpTime - self.vecSpeedLabelOutput.setText(str(int(speed))) - return x_vec, y_vec, z_vec, trans_total - - def totalExpChanged(self, text): - if text == "oscEnd" and daq_utils.beamline == "fmx": - self.sampleLifetimeReadback_ledit.setStyleSheet("color : gray") - try: - if float(str(self.osc_range_ledit.text())) == 0: - if text == "oscRange": - if self.controlEnabled(): - self.stillMode_pv.put(1) - self.colEndLabel.setText("Number of Images: ") - if ( - str(self.protoComboBox.currentText()) != "standard" - and str(self.protoComboBox.currentText()) != "vector" - ): - self.totalExptime_ledit.setText("----") - else: - try: - totalExptime = float(self.osc_end_ledit.text()) * float( - self.exp_time_ledit.text() - ) - except ValueError: - totalExptime = 0.0 - except TypeError: - totalExptime = 0.0 - except ZeroDivisionError: - totalExptime = 0.0 - self.totalExptime_ledit.setText("%.3f" % totalExptime) - return - else: - if text == "oscRange": - if self.controlEnabled(): - self.standardMode_pv.put(1) - self.colEndLabel.setText("Oscillation Range:") - except ValueError: - return - - if ( - str(self.protoComboBox.currentText()) != "standard" - and str(self.protoComboBox.currentText()) != "vector" - ): - self.totalExptime_ledit.setText("----") - else: - try: - totalExptime = ( - float(self.osc_end_ledit.text()) - / (float(self.osc_range_ledit.text())) - ) * float(self.exp_time_ledit.text()) - except ValueError: - totalExptime = 0.0 - except TypeError: - totalExptime = 0.0 - except ZeroDivisionError: - totalExptime = 0.0 - self.totalExptime_ledit.setText("%.3f" % totalExptime) - if str(self.protoComboBox.currentText()) == "vector": - try: - self.updateVectorLengthAndSpeed() - except: - pass - - try: - if float(self.osc_end_ledit.text()) >= 5.0: - self.staffScreenDialog.fastDPCheckBox.setChecked(True) - else: - self.staffScreenDialog.fastDPCheckBox.setChecked(False) - except: - pass - - def resoTextChanged(self, text): - try: - dist_s = "%.2f" % ( - daq_utils.distance_from_reso( - daq_utils.det_radius, - float(text), - daq_utils.energy2wave(float(self.energy_ledit.text())), - 0, - ) - ) - except ValueError: - dist_s = self.detDistRBVLabel.getEntry().text() - self.detDistMotorEntry.getEntry().setText(dist_s) - - def detDistTextChanged(self, text): - try: - reso_s = "%.2f" % ( - daq_utils.calc_reso( - daq_utils.det_radius, - float(text), - daq_utils.energy2wave(float(self.energy_ledit.text())), - 0, - ) - ) - except ValueError: - reso_s = "50.0" - except TypeError: - reso_s = "50.0" - self.setGuiValues({"resolution": reso_s}) - - def energyTextChanged(self, text): - dist_s = "%.2f" % ( - daq_utils.distance_from_reso( - daq_utils.det_radius, - float(self.resolution_ledit.text()), - float(text), - 0, - ) - ) - self.detDistMotorEntry.getEntry().setText(dist_s) - - # code below and its application from: https://snorfalorpagus.net/blog/2014/08/09/validating-user-input-in-pyqt4-using-qvalidator/ - def checkEntryState(self, *args, **kwargs): - sender = self.sender() - validator = sender.validator() - state = validator.validate(sender.text(), 0)[0] - if state == QtGui.QValidator.Intermediate: - color = "#fff79a" # yellow - elif state == QtGui.QValidator.Invalid: - color = "#f6989d" # red - else: - color = "#ffffff" # white - sender.setStyleSheet("QLineEdit { background-color: %s }" % color) - - def validateAllFields(self): - fields_dict = { - self.exp_time_ledit: {"name": "exposure time", "minmax": VALID_EXP_TIMES}, - self.detDistMotorEntry.getEntry(): { - "name": "detector distance", - "minmax": VALID_DET_DIST, - }, - self.totalExptime_ledit: { - "name": "total exposure time", - "minmax": VALID_TOTAL_EXP_TIMES, - }, - } - - return self.validateFields(fields_dict) - - def validateFields(self, field_values_dict): - for field, value in field_values_dict.items(): - values = value["minmax"] - field_name = value["name"] - logger.info("validateFields: %s %s %s" % (field_name, field.text(), values)) - try: - val = float(field.text()) - logger.info( - ">= min: %s <= max: %s" - % (val >= values["fmx"]["min"], val <= values["fmx"]["max"]) - ) - except: # total exposure time is '----' for rasters, so just ignore - pass - if ( - field.text() == "----" - ): # special case: total exp time not calculated for non-standard, non-vector experiments - continue - if ( - field.validator().validate(field.text(), 0)[0] - != QtGui.QValidator.Acceptable - ): - self.popupServerMessage( - "Invalid value for field %s! must be between %s and %s" - % ( - field_name, - values[daq_utils.beamline]["min"], - values[daq_utils.beamline]["max"], - ) - ) - return False - return True - - def protoRadioToggledCB(self, text): - if self.protoStandardRadio.isChecked(): - self.protoComboBox.setCurrentIndex(self.protoComboBox.findText("standard")) - self.protoComboActivatedCB(text) - elif self.protoRasterRadio.isChecked(): - self.protoComboBox.setCurrentIndex(self.protoComboBox.findText("raster")) - self.protoComboActivatedCB(text) - elif self.protoVectorRadio.isChecked(): - self.protoComboBox.setCurrentIndex(self.protoComboBox.findText("vector")) - self.protoComboActivatedCB(text) - else: - pass - - def beamsizeComboActivatedCB(self, text): - if daq_utils.beamline == "nyx": - index = self.beamsizeComboBox.findText(str(text)) - self.aperture.current_index.put(index) - else: - comm_s = 'set_beamsize("' + str(text[0:2]) + '","' + str(text[2:4]) + '")' - logger.info(comm_s) - self.send_to_server(comm_s) - - def protoComboActivatedCB(self, text): - self.showProtParams() - protocol = str(self.protoComboBox.currentText()) - if protocol in ("raster", "stepRaster", "rasterScreen", "multiCol"): - self.vidActionRasterDefRadio.setChecked(True) - else: - self.vidActionC2CRadio.setChecked(True) - if protocol == "burn": - self.staffScreenDialog.fastDPCheckBox.setChecked(False) - else: - self.staffScreenDialog.fastDPCheckBox.setChecked(True) - if protocol == "raster": - self.protoRasterRadio.setChecked(True) - self.osc_start_ledit.setEnabled(False) - self.osc_end_ledit.setEnabled(False) - self.setGuiValues( - { - "osc_range": getBlConfig("rasterDefaultWidth"), - "exp_time": getBlConfig("rasterDefaultTime"), - "transmission": getBlConfig("rasterDefaultTrans"), - } - ) - elif protocol == "rasterScreen": - self.osc_start_ledit.setEnabled(False) - self.osc_end_ledit.setEnabled(False) - self.setGuiValues( - { - "osc_range": getBlConfig("rasterDefaultWidth"), - "exp_time": getBlConfig("rasterDefaultTime"), - "transmission": getBlConfig("rasterDefaultTrans"), - } - ) - self.protoOtherRadio.setChecked(True) - elif protocol == "standard": - self.protoStandardRadio.setChecked(True) - self.setGuiValues( - { - "osc_range": getBlConfig("screen_default_width"), - "exp_time": getBlConfig("screen_default_time"), - "transmission": getBlConfig("stdTrans"), - } - ) - self.osc_start_ledit.setEnabled(True) - self.osc_end_ledit.setEnabled(True) - if daq_utils.beamline == "fmx": - self.calcLifetimeCB() - elif protocol == "burn": - self.setGuiValues( - { - "osc_range": "0.0", - "exp_time": getBlConfig("burnDefaultTime"), - "transmission": getBlConfig("burnDefaultTrans"), - } - ) - screenWidth = float(getBlConfig("burnDefaultNumFrames")) - self.setGuiValues({"osc_end": screenWidth}) - self.osc_start_ledit.setEnabled(True) - self.osc_end_ledit.setEnabled(True) - - elif protocol == "vector": - self.setGuiValues( - { - "osc_range": getBlConfig("screen_default_width"), - "exp_time": getBlConfig("screen_default_time"), - "transmission": getBlConfig("stdTrans"), - } - ) - self.osc_start_ledit.setEnabled(True) - self.osc_end_ledit.setEnabled(True) - self.protoVectorRadio.setChecked(True) - if daq_utils.beamline == "fmx": - self.calcLifetimeCB() - else: - self.protoOtherRadio.setChecked(True) - self.totalExpChanged("") - - def rasterEvalComboActivatedCB(self, text): - db_lib.beamlineInfo( - daq_utils.beamline, - "rasterScoreFlag", - info_dict={"index": self.rasterEvalComboBox.findText(str(text))}, - ) - if self.currentRasterCellList != []: - self.reFillPolyRaster() - - def popBaseDirectoryDialogCB(self): - fname = QtWidgets.QFileDialog.getExistingDirectory( - self, "Choose Directory", "", QtWidgets.QFileDialog.DontUseNativeDialog - ) - if fname != "": - self.dataPathGB.setBasePath_ledit(fname) - - def popImportDialogCB(self): - #self.timerSample.stop() - fname = QtWidgets.QFileDialog.getOpenFileName( - self, - "Choose Spreadsheet File", - "", - filter="*.xls *.xlsx", - options=QtWidgets.QFileDialog.DontUseNativeDialog, - ) - #self.timerSample.start(SAMPLE_TIMER_DELAY) - if fname != "": - logger.info(fname) - comm_s = f'importSpreadsheet("{fname[0]}", "{daq_utils.owner}")' - logger.info(comm_s) - self.send_to_server(comm_s) - - def setUserModeCB(self): - self.vidActionDefineCenterRadio.setEnabled(False) - - def setExpertModeCB(self): - self.vidActionDefineCenterRadio.setEnabled(True) - - def upPriorityCB( - self, - ): # neither of these are very elegant, and might even be glitchy if overused - currentPriority = self.selectedSampleRequest["priority"] - if currentPriority < 1: - return - orderedRequests = db_lib.getOrderedRequestList(daq_utils.beamline) - for i in range(len(orderedRequests)): - if orderedRequests[i]["sample"] == self.selectedSampleRequest["sample"]: - if i < 2: - self.topPriorityCB() - else: - priority = ( - orderedRequests[i - 2]["priority"] - + orderedRequests[i - 1]["priority"] - ) / 2 - if currentPriority == priority: - priority = priority + 20 - db_lib.updatePriority(self.selectedSampleRequest["uid"], priority) - self.treeChanged_pv.put(1) - - def downPriorityCB(self): - currentPriority = self.selectedSampleRequest["priority"] - if currentPriority < 1: - return - orderedRequests = db_lib.getOrderedRequestList(daq_utils.beamline) - for i in range(len(orderedRequests)): - if orderedRequests[i]["sample"] == self.selectedSampleRequest["sample"]: - if (len(orderedRequests) - i) < 3: - self.bottomPriorityCB() - else: - priority = ( - orderedRequests[i + 1]["priority"] - + orderedRequests[i + 2]["priority"] - ) / 2 - if currentPriority == priority: - priority = priority - 20 - db_lib.updatePriority(self.selectedSampleRequest["uid"], priority) - self.treeChanged_pv.put(1) - - def topPriorityCB(self): - currentPriority = self.selectedSampleRequest["priority"] - if currentPriority < 1: - return - priority = int(self.getMaxPriority()) - priority = priority + 100 - db_lib.updatePriority(self.selectedSampleRequest["uid"], priority) - self.treeChanged_pv.put(1) - - def bottomPriorityCB(self): - currentPriority = self.selectedSampleRequest["priority"] - if currentPriority < 1: - return - priority = int(self.getMinPriority()) - priority = priority - 100 - db_lib.updatePriority(self.selectedSampleRequest["uid"], priority) - self.treeChanged_pv.put(1) - - def dewarViewToggledCB(self, identifier): - self.selectedSampleRequest = {} - # should probably clear textfields here too - if identifier == "dewarView": - if self.dewarViewRadio.isChecked(): - self.dewarTree.refreshTreeDewarView() - else: - if self.priorityViewRadio.isChecked(): - self.dewarTree.refreshTreePriorityView() - - def dewarViewToggleCheckCB(self): - if self.dewarViewRadio.isChecked(): - self.dewarTree.refreshTreeDewarView() - else: - self.dewarTree.refreshTreePriorityView() - - def moveOmegaCB(self): - comm_s = ( - 'omegaMoveAbs(' - + str(self.sampleOmegaMoveLedit.getEntry().text()) - + ")" - ) - logger.info(comm_s) - self.send_to_server(comm_s) - - def moveEnergyCB(self): - energyRequest = float(str(self.energy_ledit.text())) - if abs(energyRequest - self.energy_pv.get()) > 10.0: - self.popupServerMessage("Energy change must be less than 10 ev") - return - else: - comm_s = 'mvaDescriptor("energy",' + str(self.energy_ledit.text()) + ")" - logger.info(comm_s) - self.send_to_server(comm_s) - - def setLifetimeCB(self, lifetime): - if hasattr(self, "sampleLifetimeReadback_ledit"): - self.sampleLifetimeReadback_ledit.setText(f"{lifetime:.2f}") - self.sampleLifetimeReadback_ledit.setStyleSheet("color : black") - - def calcLifetimeCB(self): - self.raddoseTimer.start() - if hasattr(self, "sampleLifetimeReadback_ledit"): - self.sampleLifetimeReadback_ledit.setStyleSheet("color : gray") - - def spawnRaddoseThread(self): - if not os.path.exists("2vb1.pdb"): - os.system("cp -a $CONFIGDIR/2vb1.pdb .") - os.system("mkdir rd3d") - energyReadback = self.energy_pv.get() / 1000.0 - sampleFlux = self.sampleFluxPV.get() - if hasattr(self, "transmission_ledit") and hasattr( - self, "transmissionReadback_ledit" - ): - try: - sampleFlux = ( - sampleFlux * float(self.transmission_ledit.text()) - ) / float(self.transmissionReadback_ledit.text()) - except Exception as e: - logger.info(f"Exception while calculating sample flux {e}") - logger.info("sample flux = " + str(sampleFlux)) - # Read vector length only if the vector protocol is chosen - vecLen = 0 - if self.protoVectorRadio.isChecked(): - try: - vecLen = float(self.vecLenLabelOutput.text()) - except: - pass - - try: - wedge = float(self.osc_end_ledit.text()) - raddose_thread = RaddoseThread( - parent=self, - beamsizeV=3.0, - beamsizeH=5.0, - vectorL=vecLen, - energy=energyReadback, - wedge=wedge, - flux=sampleFlux, - verbose=True, - ) - raddose_thread.lifetime.connect( - lambda lifetime: self.setLifetimeCB(lifetime) - ) - raddose_thread.start() - - except: - lifeTime_s = "0.00" - - def setTransCB(self): - try: - if ( - float(self.transmission_ledit.text()) > 1.0 - or float(self.transmission_ledit.text()) < 0.001 - ): - self.popupServerMessage("Transmission must be 0.001-1.0") - return - except ValueError as e: - self.popupServerMessage("Please enter a valid number") - return - comm_s = "setTrans(" + str(self.transmission_ledit.text()) + ")" - logger.info(comm_s) - self.send_to_server(comm_s) - - def setDCStartCB(self): - currentPos = float(self.sampleOmegaRBVLedit.getEntry().text()) % 360.0 - self.setGuiValues({"osc_start": currentPos}) - - def moveDetDistCB(self): - comm_s = ( - 'mvaDescriptor("detectorDist",' - + str(self.detDistMotorEntry.getEntry().text()) - + ")" - ) - logger.info(comm_s) - self.send_to_server(comm_s) - - def omegaTweakNegCB(self): - tv = float(self.omegaTweakVal_ledit.text()) - tweakVal = 0.0 - tv - if self.controlEnabled(): - mv_status = self.gon.omega.move(self.gon.omega.val() + tweakVal) - else: - self.popupServerMessage("You don't have control") - - def omegaTweakPosCB(self): - tv = float(self.omegaTweakVal_ledit.text()) - if self.controlEnabled(): - mv_status = self.gon.omega.move(self.gon.omega.val() + tv) - else: - self.popupServerMessage("You don't have control") - - def focusTweakCB(self, tv): - tvf = float(tv) * daq_utils.unitScaling - - current_viewangle = daq_utils.mag1ViewAngle - if self.zoom2Radio.isChecked(): - current_viewangle = daq_utils.mag2ViewAngle - elif self.zoom3Radio.isChecked(): - current_viewangle = daq_utils.mag3ViewAngle - elif self.zoom4Radio.isChecked(): - current_viewangle = daq_utils.mag4ViewAngle - - if current_viewangle == daq_utils.CAMERA_ANGLE_BEAM: - view_omega_offset = 0 - elif current_viewangle == daq_utils.CAMERA_ANGLE_BELOW: - view_omega_offset = 90 - elif current_viewangle == daq_utils.CAMERA_ANGLE_ABOVE: - view_omega_offset = -90 - - if self.controlEnabled(): - tvY = tvf * ( - math.cos(math.radians(view_omega_offset + self.gon.omega.val())) - ) # these are opposite C2C - tvZ = tvf * ( - math.sin(math.radians(view_omega_offset + self.gon.omega.val())) - ) - self.gon.y.move(self.gon.y.val() + tvY) - self.gon.z.move(self.gon.z.val() + tvZ) - else: - self.popupServerMessage("You don't have control") - - def omegaTweakCB(self, tv): - tvf = float(tv) - if self.controlEnabled(): - status = self.gon.omega.move(self.gon.omega.val() + tvf) - status.wait() - else: - self.popupServerMessage("You don't have control") - - def autoCenterLoopCB(self): - logger.info("auto center loop") - self.send_to_server("loop_center_xrec()") - - def autoRasterLoopCB(self): - self.selectedSampleID = self.selectedSampleRequest["sample"] - comm_s = "autoRasterLoop(" + str(self.selectedSampleID) + ")" - self.send_to_server(comm_s) - - def runRastersCB(self): - comm_s = "snakeRaster(" + str(self.selectedSampleRequest["uid"]) + ")" - self.send_to_server(comm_s) - - def drawInteractiveRasterCB(self): # any polygon for now, interactive or from xrec - for i in range(len(self.polyPointItems)): - self.scene.removeItem(self.polyPointItems[i]) - polyPointItems = [] - pen = QtGui.QPen(QtCore.Qt.red) - brush = QtGui.QBrush(QtCore.Qt.red) - points = [] - polyPoints = [] - if self.click_positions != []: # use the user clicks - if len(self.click_positions) == 2: # draws a single row or column - logger.info("2-click raster") - polyPoints.append(self.click_positions[0]) - point = QtCore.QPointF( - self.click_positions[0].x(), self.click_positions[1].y() - ) - polyPoints.append(point) - point = QtCore.QPointF( - self.click_positions[0].x() + 2, self.click_positions[1].y() - ) - polyPoints.append(point) - point = QtCore.QPointF( - self.click_positions[0].x() + 2, self.click_positions[0].y() - ) - polyPoints.append(point) - self.rasterPoly = QtWidgets.QGraphicsPolygonItem( - QtGui.QPolygonF(polyPoints) - ) - else: - self.rasterPoly = QtWidgets.QGraphicsPolygonItem( - QtGui.QPolygonF(self.click_positions) - ) - else: - return - self.polyBoundingRect = self.rasterPoly.boundingRect() - raster_w = int(self.polyBoundingRect.width()) - raster_h = int(self.polyBoundingRect.height()) - center_x = int(self.polyBoundingRect.center().x()) - center_y = int(self.polyBoundingRect.center().y()) - stepsizeXPix = self.screenXmicrons2pixels(float(self.rasterStepEdit.text())) - stepsizeYPix = self.screenYmicrons2pixels(float(self.rasterStepEdit.text())) - self.click_positions = [] - self.definePolyRaster( - raster_w, raster_h, stepsizeXPix, stepsizeYPix, center_x, center_y - ) - - def measurePolyCB(self): - for i in range(len(self.polyPointItems)): - self.scene.removeItem(self.polyPointItems[i]) - if self.measureLine != None: - self.scene.removeItem(self.measureLine) - self.polyPointItems = [] - - pen = QtGui.QPen(QtCore.Qt.red) - brush = QtGui.QBrush(QtCore.Qt.red) - points = [] - if self.click_positions != []: # use the user clicks - if len(self.click_positions) == 2: # draws a single row or column - self.measureLine = self.scene.addLine( - self.click_positions[0].x(), - self.click_positions[0].y(), - self.click_positions[1].x(), - self.click_positions[1].y(), - pen, - ) - length = self.measureLine.line().length() - fov = self.getCurrentFOV() - lineMicronsX = int(round(length * (fov["x"] / daq_utils.screenPixX))) - logger.info("linelength = " + str(lineMicronsX)) - self.click_positions = [] - - def center3LoopCB(self): - logger.info("3-click center loop") - self.threeClickCount = 1 - self.click3Button.setStyleSheet("background-color: yellow") - if(daq_utils.exporter_enabled): - self.md2.exporter.cmd("startManualSampleCentring", "") - else: - self.send_to_server('mvaDescriptor("omega",0)') - - def fillPolyRaster( - self, rasterReq, waitTime=1 - ): # at this point I should have a drawn polyRaster - time.sleep(waitTime) - logger.info("filling poly for " + str(rasterReq["uid"])) - resultCount = len(db_lib.getResultsforRequest(rasterReq["uid"])) - rasterResults = db_lib.getResultsforRequest(rasterReq["uid"]) - rasterResult = {} - for i in range(0, len(rasterResults)): - if rasterResults[i]["result_type"] == "rasterResult": - rasterResult = rasterResults[i] - break - try: - rasterDef = rasterReq["request_obj"]["rasterDef"] - except KeyError: - db_lib.deleteRequest(rasterReq["uid"]) - return - rasterListIndex = 0 - for i in range(len(self.rasterList)): - if self.rasterList[i] != None: - if self.rasterList[i]["uid"] == rasterReq["uid"]: - rasterListIndex = i - if rasterResult == {}: - return - - try: - currentRasterGroup = self.rasterList[rasterListIndex]["graphicsItem"] - except IndexError as e: - logger.error("IndexError while getting raster group: %s" % e) - return - self.currentRasterCellList = currentRasterGroup.childItems() - cellResults = rasterResult["result_obj"]["rasterCellResults"]["resultObj"] - numLines = len(cellResults) - cellResults_array = [{} for i in range(numLines)] - my_array = np.zeros(numLines) - spotLineCounter = 0 - cellIndex = 0 - rowStartIndex = 0 - rasterEvalOption = str(self.rasterEvalComboBox.currentText()) - lenX = abs( - rasterDef["rowDefs"][0]["end"]["x"] - rasterDef["rowDefs"][0]["start"]["x"] - ) # ugly for tile flip/noflip - for i in range( - len(rasterDef["rowDefs"]) - ): # this is building up "my_array" with the rasterEvalOption result, and numpy can then be run against the array. 2/16, I think cellResultsArray not needed - rowStartIndex = spotLineCounter - numsteps = rasterDef["rowDefs"][i]["numsteps"] - for j in range(numsteps): - try: - cellResult = cellResults[spotLineCounter] - except IndexError: - logger.error("caught index error #1") - logger.error("numlines = " + str(numLines)) - logger.error( - "expected: " + str(len(rasterDef["rowDefs"]) * numsteps) - ) - return # means a raster failure, and not enough data to cover raster, caused a gui crash - try: - spotcount = cellResult["spot_count_no_ice"] - filename = cellResult["image"] - except TypeError: - spotcount = 0 - filename = "empty" - - if ( - lenX > 180 and self.scannerType == "PI" - ): # this is trying to figure out row direction - cellIndex = spotLineCounter - else: - if i % 2 == 0: # this is trying to figure out row direction - cellIndex = spotLineCounter - else: - cellIndex = rowStartIndex + ((numsteps - 1) - j) - try: - if rasterEvalOption == "Spot Count": - my_array[cellIndex] = spotcount - elif rasterEvalOption == "Intensity": - my_array[cellIndex] = cellResult["total_intensity"] - else: - if float(cellResult["d_min"]) == -1: - my_array[cellIndex] = 50.0 - else: - my_array[cellIndex] = float(cellResult["d_min"]) - except IndexError: - logger.error("caught index error #2") - logger.error("numlines = " + str(numLines)) - logger.error( - "expected: " + str(len(rasterDef["rowDefs"]) * numsteps) - ) - return # means a raster failure, and not enough data to cover raster, caused a gui crash - cellResults_array[ - cellIndex - ] = cellResult # instead of just grabbing filename, get everything. Not sure why I'm building my own list of results. How is this different from cellResults? - # I don't think cellResults_array is different from cellResults, could maybe test that below by subtituting one for the other. It may be a remnant of trying to store less than the whole result set. - spotLineCounter += 1 - floor = np.amin(my_array) - ceiling = np.amax(my_array) - cellCounter = 0 - for i in range(len(rasterDef["rowDefs"])): - rowCellCount = 0 - for j in range(rasterDef["rowDefs"][i]["numsteps"]): - cellResult = cellResults_array[cellCounter] - try: - spotcount = int(cellResult["spot_count_no_ice"]) - cellFilename = cellResult["image"] - d_min = float(cellResult["d_min"]) - if d_min == -1: - d_min = 50.0 # trying to handle frames with no spots - total_intensity = int(cellResult["total_intensity"]) - except TypeError: - spotcount = 0 - cellFilename = "empty" - d_min = 50.0 - total_intensity = 0 - - if rasterEvalOption == "Spot Count": - param = spotcount - elif rasterEvalOption == "Intensity": - param = total_intensity - else: - param = d_min - if ceiling == 0: - color_id = 255 - elif ceiling == floor: - if rasterEvalOption == "Resolution": - color_id = 0 - else: - color_id = 255 - elif rasterEvalOption == "Resolution": - color_id = int( - 255.0 * (float(param - floor) / float(ceiling - floor)) - ) - else: - color_id = int( - 255 - (255.0 * (float(param - floor) / float(ceiling - floor))) - ) - self.currentRasterCellList[cellCounter].setBrush( - QtGui.QBrush(QtGui.QColor(0, 255 - color_id, 0, 127)) - ) - self.currentRasterCellList[cellCounter].setData(0, spotcount) - self.currentRasterCellList[cellCounter].setData(1, cellFilename) - self.currentRasterCellList[cellCounter].setData(2, d_min) - self.currentRasterCellList[cellCounter].setData(3, total_intensity) - cellCounter += 1 - - def takeRasterSnapshot(self, rasterReq): - request_obj = rasterReq["request_obj"] - directory = request_obj["directory"] - filePrefix = request_obj["file_prefix"] - basePath = request_obj["basePath"] - visitName = daq_utils.getVisitName() - jpegDirectory = ( - visitName - + "/jpegs/" - + directory[directory.find(visitName) + len(visitName) : len(directory)] - ) - fullJpegDirectory = basePath + "/" + jpegDirectory - if not os.path.exists(fullJpegDirectory): - os.system("mkdir -p " + fullJpegDirectory) - jpegImagePrefix = fullJpegDirectory + "/" + filePrefix - jpegImageFilename = jpegImagePrefix + ".jpg" - jpegImageThumbFilename = jpegImagePrefix + "t.jpg" - logger.info("saving raster snapshot") - self.saveVidSnapshotCB( - "Raster Result from sample " + str(rasterReq["request_obj"]["file_prefix"]), - useOlog=False, - reqID=rasterReq["uid"], - rasterHeatJpeg=jpegImageFilename, - ) - self.saveVidSnapshotCB( - "Raster Result from sample " + str(rasterReq["request_obj"]["file_prefix"]), - useOlog=False, - reqID=rasterReq["uid"], - rasterHeatJpeg=jpegImageFilename, - ) - self.send_to_server(f"ispybLib.insertRasterResult('{rasterReq['uid']}', '{visitName}')") - - def reFillPolyRaster(self): - rasterEvalOption = str(self.rasterEvalComboBox.currentText()) - for i in range(len(self.rasterList)): - if self.rasterList[i] != None: - currentRasterGroup = self.rasterList[i]["graphicsItem"] - currentRasterCellList = currentRasterGroup.childItems() - my_array = np.zeros(len(currentRasterCellList)) - for i in range( - 0, len(currentRasterCellList) - ): # first loop is to get floor and ceiling - cellIndex = i - if rasterEvalOption == "Spot Count": - spotcount = currentRasterCellList[i].data(0) - if not isinstance(spotcount, int): - spotcount = int(spotcount) - my_array[cellIndex] = spotcount - elif rasterEvalOption == "Intensity": - total_intensity = currentRasterCellList[i].data(3) - if not isinstance(total_intensity, int): - total_intensity = int(total_intensity) - my_array[cellIndex] = total_intensity - else: - d_min = currentRasterCellList[i].data(2) - if not isinstance(d_min, float): - d_min = float(d_min) - if d_min == -1: - d_min = 50.0 # trying to handle frames with no spots - my_array[cellIndex] = d_min - floor = np.amin(my_array) - ceiling = np.amax(my_array) - for i in range(0, len(currentRasterCellList)): - if (rasterEvalOption == "Spot Count") or ( - rasterEvalOption == "Intensity" - ): - param = my_array[i] - else: - d_min = my_array[i] - if d_min == -1: - d_min = 50.0 # trying to handle frames with no spots - param = d_min - if ceiling == 0: - color_id = 255 - elif ceiling == floor: - if rasterEvalOption == "Resolution": - color_id = 0 - else: - color_id = 255 - elif rasterEvalOption == "Resolution": - color_id = int( - 255.0 * (float(param - floor) / float(ceiling - floor)) - ) - else: - color_id = int( - 255 - - (255.0 * (float(param - floor) / float(ceiling - floor))) - ) - currentRasterCellList[i].setBrush( - QtGui.QBrush(QtGui.QColor(0, 255 - color_id, 0, 127)) - ) - - def saveCenterCB(self): - pen = QtGui.QPen(QtCore.Qt.magenta) - brush = QtGui.QBrush(QtCore.Qt.magenta) - markWidth = 10 - marker = self.scene.addEllipse( - self.centerMarker.x() - - (markWidth / 2.0) - - 1 - + self.centerMarkerCharOffsetX, - self.centerMarker.y() - - (markWidth / 2.0) - - 1 - + self.centerMarkerCharOffsetY, - markWidth, - markWidth, - pen, - brush, - ) - marker.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True) - self.centeringMark = { - "sampCoords": { - "x": self.gon.x.val(), - "y": self.gon.y.val(), - "z": self.gon.z.val(), - }, - "graphicsItem": marker, - "centerCursorX": self.centerMarker.x(), - "centerCursorY": self.centerMarker.y(), - } - self.centeringMarksList.append(self.centeringMark) - - def selectAllCenterCB(self): - logger.info("select all center") - for i in range(len(self.centeringMarksList)): - self.centeringMarksList[i]["graphicsItem"].setSelected(True) - - def lightUpCB(self): - self.send_to_server("backlightBrighter()") - - def lightDimCB(self): - self.send_to_server("backlightDimmer()") - - def eraseRastersCB(self): - if self.rasterList != []: - for i in range(len(self.rasterList)): - if self.rasterList[i] != None: - self.scene.removeItem(self.rasterList[i]["graphicsItem"]) - self.rasterList = [] - self.rasterDefList = [] - self.currentRasterCellList = [] - - def eraseCB(self): - self.click_positions = [] - if self.measureLine != None: - self.scene.removeItem(self.measureLine) - for i in range(len(self.centeringMarksList)): - self.scene.removeItem(self.centeringMarksList[i]["graphicsItem"]) - self.centeringMarksList = [] - for i in range(len(self.polyPointItems)): - self.scene.removeItem(self.polyPointItems[i]) - self.polyPointItems = [] - if self.rasterList != []: - for i in range(len(self.rasterList)): - if self.rasterList[i] != None: - self.scene.removeItem(self.rasterList[i]["graphicsItem"]) - self.rasterList = [] - self.rasterDefList = [] - self.currentRasterCellList = [] - self.clearVectorCB() - if self.rasterPoly != None: - self.scene.removeItem(self.rasterPoly) - self.rasterPoly = None - - def eraseDisplayCB( - self, - ): # use this for things like zoom change. This is not the same as getting rid of all rasters. - if self.rasterList != []: - for i in range(len(self.rasterList)): - if self.rasterList[i] != None: - self.scene.removeItem(self.rasterList[i]["graphicsItem"]) - self.rasterList = [] - return # short circuit - if self.rasterPoly != None: - self.scene.removeItem(self.rasterPoly) - - def getCurrentFOV(self): - fov = {"x": 0.0, "y": 0.0} - if self.zoom2Radio.isChecked(): # lowmagzoom - if ( - daq_utils.sampleCameraCount == 2 - ): # this is a hard assumption that when there are 2 cameras the second uses highmagfov - fov["x"] = daq_utils.highMagFOVx - fov["y"] = daq_utils.highMagFOVy - else: - fov["x"] = daq_utils.lowMagFOVx / 2.0 - fov["y"] = daq_utils.lowMagFOVy / 2.0 - elif self.zoom1Radio.isChecked(): - fov["x"] = daq_utils.lowMagFOVx - fov["y"] = daq_utils.lowMagFOVy - elif self.zoom4Radio.isChecked(): - fov["x"] = daq_utils.highMagFOVx / 2.0 - fov["y"] = daq_utils.highMagFOVy / 2.0 - else: - fov["x"] = daq_utils.highMagFOVx - fov["y"] = daq_utils.highMagFOVy - return fov - - def screenXPixels2microns(self, pixels): - img_scale_factor = self.getMD2ImageXRatio() - pixels_per_mm = 1 / self.camera.scale_x.get() - pixels_per_micron = pixels_per_mm / 1000.0 - return float(pixels * img_scale_factor) / pixels_per_micron - print(f"pixels per micron = {pixels_per_micron}") - - def screenYPixels2microns(self, pixels): - pixels_per_mm = 1 / self.camera.scale_y.get() - pixels_per_micron = pixels_per_mm / 1000.0 - img_scale_factor = self.getMD2ImageYRatio() - return float(pixels * img_scale_factor) / pixels_per_micron - - def screenXmicrons2pixels(self, microns): - pixels_per_mm = 1 / self.camera.scale_x.get() - pixels_per_micron = pixels_per_mm / 1000.0 - img_scale_factor = self.getMD2ImageXRatio() - return float(microns * pixels_per_micron) / img_scale_factor - - def screenYmicrons2pixels(self, microns): - pixels_per_mm = 1 / self.camera.scale_y.get() - pixels_per_micron = pixels_per_mm / 1000.0 - img_scale_factor = self.getMD2ImageYRatio() - return float(microns * pixels_per_micron) / img_scale_factor - - def getMD2ImageXRatio(self): - md2_img_width = daq_utils.highMagPixX - lsdc_img_width = daq_utils.screenPixX - return float(md2_img_width) / float(lsdc_img_width) - - def getMD2ImageYRatio(self): - md2_img_height = daq_utils.highMagPixY - lsdc_img_height = daq_utils.screenPixY - return float(md2_img_height) / float(lsdc_img_height) - - def getMD2BeamCenterX(self): - return self.md2.center_pixel_x.get() / self.getMD2ImageXRatio() - - def getMD2BeamCenterY(self): - return self.md2.center_pixel_y.get() / self.getMD2ImageYRatio() - - def definePolyRaster( - self, raster_w, raster_h, stepsizeXPix, stepsizeYPix, point_x, point_y - ): # all come in as pixels, raster_w and raster_h are bounding box of drawn graphic - # raster status - 0=nothing done, 1=run, 2=displayed - stepTime = float(self.exp_time_ledit.text()) - stepsize = float(self.rasterStepEdit.text()) - if (stepsize / 1000.0) / stepTime > 2.0: - self.popupServerMessage( - "Stage speed exceeded. Increase exposure time, or decrease step size. Limit is 2mm/s." - ) - self.eraseCB() - return - - try: - beamWidth = float(self.beamWidth_ledit.text()) - beamHeight = float(self.beamHeight_ledit.text()) - except ValueError: - logger.error("bad value for beam width or beam height") - self.popupServerMessage("bad value for beam width or beam height") - return - if self.scannerType == "PI": - rasterDef = { - "rasterType": "normal", - "beamWidth": beamWidth, - "beamHeight": beamHeight, - "status": RasterStatus.NEW.value, - "x": self.gon.x.val() + self.sampFineX_pv.get(), - "y": self.gon.y.val() + self.sampFineY_pv.get(), - "z": self.gon.z.val() + self.sampFineZ_pv.get(), - "omega": self.gon.omega.val(), - "stepsize": stepsize, - "rowDefs": [], - } # just storing step as microns, not using her - else: - rasterDef = { - "rasterType": "normal", - "beamWidth": beamWidth, - "beamHeight": beamHeight, - "status": RasterStatus.NEW.value, - "x": self.gon.x.val(), - "y": self.gon.y.val(), - "z": self.gon.z.val(), - "omega": self.gon.omega.val(), - "stepsize": stepsize, - "rowDefs": [], - } # just storing step as microns, not using here - numsteps_h = int( - raster_w / stepsizeXPix - ) # raster_w = width,goes to numsteps horizonatl - numsteps_v = int(raster_h / stepsizeYPix) - if numsteps_h == 2: - numsteps_h = 1 # fix slop in user single line attempt - if numsteps_h % 2 == 0: # make odd numbers of rows and columns - numsteps_h = numsteps_h + 1 - if numsteps_v % 2 == 0: - numsteps_v = numsteps_v + 1 - rasterDef["numCells"] = numsteps_h * numsteps_v - point_offset_x = -(numsteps_h * stepsizeXPix) / 2 - point_offset_y = -(numsteps_v * stepsizeYPix) / 2 - if (numsteps_h == 1) or ( - numsteps_v > numsteps_h and getBlConfig("vertRasterOn") - ): # vertical raster - for i in range(numsteps_h): - rowCellCount = 0 - for j in range(numsteps_v): - newCellX = point_x + (i * stepsizeXPix) + point_offset_x - newCellY = point_y + (j * stepsizeYPix) + point_offset_y - if rowCellCount == 0: # start of a new row - rowStartX = newCellX - rowStartY = newCellY - rowCellCount = rowCellCount + 1 - if ( - rowCellCount != 0 - ): # test for no points in this row of the bounding rect are in the poly? - vectorStartX = self.screenXPixels2microns( - rowStartX - self.centerMarker.x() - self.centerMarkerCharOffsetX - ) - vectorEndX = vectorStartX - vectorStartY = self.screenYPixels2microns( - rowStartY - self.centerMarker.y() - self.centerMarkerCharOffsetY - ) - vectorEndY = vectorStartY + self.screenYPixels2microns( - rowCellCount * stepsizeYPix - ) - newRowDef = { - "start": {"x": vectorStartX, "y": vectorStartY}, - "end": {"x": vectorEndX, "y": vectorEndY}, - "numsteps": rowCellCount, - } - rasterDef["rowDefs"].append(newRowDef) - else: # horizontal raster - for i in range(numsteps_v): - rowCellCount = 0 - for j in range(numsteps_h): - newCellX = point_x + (j * stepsizeXPix) + point_offset_x - newCellY = point_y + (i * stepsizeYPix) + point_offset_y - if rowCellCount == 0: # start of a new row - rowStartX = newCellX - rowStartY = newCellY - rowCellCount = rowCellCount + 1 - if ( - rowCellCount != 0 - ): # testing for no points in this row of the bounding rect are in the poly? - vectorStartX = self.screenXPixels2microns( - rowStartX - self.centerMarker.x() - self.centerMarkerCharOffsetX - ) - vectorEndX = vectorStartX + self.screenXPixels2microns( - rowCellCount * stepsizeXPix - ) # this looks better - vectorStartY = self.screenYPixels2microns( - rowStartY - self.centerMarker.y() - self.centerMarkerCharOffsetY - ) - vectorEndY = vectorStartY - newRowDef = { - "start": {"x": vectorStartX, "y": vectorStartY}, - "end": {"x": vectorEndX, "y": vectorEndY}, - "numsteps": rowCellCount, - } - rasterDef["rowDefs"].append(newRowDef) - setBlConfig("rasterDefaultWidth", float(self.osc_range_ledit.text())) - setBlConfig("rasterDefaultTime", float(self.exp_time_ledit.text())) - setBlConfig("rasterDefaultTrans", float(self.transmission_ledit.text())) - - self.addSampleRequestCB(rasterDef) - return # short circuit - - def rasterIsDrawn(self, rasterReq): - for i in range(len(self.rasterList)): - if self.rasterList[i] != None: - if self.rasterList[i]["uid"] == rasterReq["uid"]: - return True - return False - - def drawPolyRaster( - self, rasterReq, x=-1, y=-1, z=-1 - ): # rasterDef in microns,offset from center, need to convert to pixels to draw, mainly this is for displaying autoRasters, but also called in zoom change - try: - rasterDef = rasterReq["request_obj"]["rasterDef"] - except KeyError: - return - beamSize = self.screenXmicrons2pixels(rasterDef["beamWidth"]) - stepsizeX = self.screenXmicrons2pixels(rasterDef["stepsize"]) - stepsizeY = self.screenYmicrons2pixels(rasterDef["stepsize"]) - pen = QtGui.QPen(QtCore.Qt.red) - newRasterCellList = [] - try: - if ( - rasterDef["rowDefs"][0]["start"]["y"] - == rasterDef["rowDefs"][0]["end"]["y"] - ): # this is a horizontal raster - rasterDir = "horizontal" - else: - rasterDir = "vertical" - except IndexError: - return - for i in range(len(rasterDef["rowDefs"])): - rowCellCount = 0 - for j in range(rasterDef["rowDefs"][i]["numsteps"]): - if rasterDir == "horizontal": - newCellX = ( - self.screenXmicrons2pixels( - rasterDef["rowDefs"][i]["start"]["x"] - ) - + (j * stepsizeX) - + self.centerMarker.x() - + self.centerMarkerCharOffsetX - ) - newCellY = ( - self.screenYmicrons2pixels( - rasterDef["rowDefs"][i]["start"]["y"] - ) - + self.centerMarker.y() - + self.centerMarkerCharOffsetY - ) - else: - newCellX = ( - self.screenXmicrons2pixels( - rasterDef["rowDefs"][i]["start"]["x"] - ) - + self.centerMarker.x() - + self.centerMarkerCharOffsetX - ) - newCellY = ( - self.screenYmicrons2pixels( - rasterDef["rowDefs"][i]["start"]["y"] - ) - + (j * stepsizeY) - + self.centerMarker.y() - + self.centerMarkerCharOffsetY - ) - if rowCellCount == 0: # start of a new row - rowStartX = newCellX - rowStartY = newCellY - newCellX = int(newCellX) - newCellY = int(newCellY) - newCell = RasterCell(newCellX, newCellY, stepsizeX, stepsizeY, self) - newRasterCellList.append(newCell) - newCell.setPen(pen) - rowCellCount = rowCellCount + 1 # really just for test of new row - newItemGroup = RasterGroup(self) - self.scene.addItem(newItemGroup) - for i in range(len(newRasterCellList)): - newItemGroup.addToGroup(newRasterCellList[i]) - newRasterGraphicsDesc = { - "uid": rasterReq["uid"], - "coords": { - "x": rasterDef["x"], - "y": rasterDef["y"], - "z": rasterDef["z"], - "omega": rasterDef["omega"], - }, - "graphicsItem": newItemGroup, - } - self.rasterList.append(newRasterGraphicsDesc) - - def sampleFrameCB(self): - frames = str(self.capture.get(cv2.CAP_PROP_BUFFERSIZE)) - text = frames + " frames" - self.imageScaleText = self.scene.addSimpleText( - text, font=QtGui.QFont("Times", 13) - ) - ''' - for thread in self.active_camera_threads: - if not thread.is_alive(): # remove old threads - self.active_camera_threads.remove(thread) - if not len(self.active_camera_threads) > 0: - self.active_camera_threads.append(threading.Thread(target=self.timerSampleRefresh)) - self.active_camera_threads[-1].start() - - if not self.frame_queue.empty(): - self.pixmap_item.setPixmap(self.frame_queue.get()) - ''' - def timerSampleRefresh(self): - if self.capture is None: - return - # uncomment this for frame resizing - # self.currentFrame = self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640) - # self.currentFrame = self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 512) - start_time = time.time() - retval, self.currentFrame = self.capture.read() - self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 1) - capture_time = time.time() - if self.currentFrame is None: - logger.debug( - "no frame read from stream URL - ensure the URL does not end with newline and that the filename is correct" - ) - return # maybe stop the timer also??? - height, width = self.currentFrame.shape[:2] - qimage = QtGui.QImage( - self.currentFrame, width, height, 3 * width, QtGui.QImage.Format_RGB888 - ) - qimage = qimage.rgbSwapped() - pixmap_orig = QtGui.QPixmap.fromImage(qimage) - self.pixmap_item.setPixmap(pixmap_orig) - #while(self.frame_queue.qsize() > 1): - # self.frame_queue.get() # remove old frames - #self.frame_queue.put(pixmap_orig) - end_time = time.time() - #logger.info(f"capture time: {capture_time - start_time}, total time: {end_time - start_time}") - - def sceneKey(self, event): - if ( - event.key() == QtCore.Qt.Key_Delete - or event.key() == QtCore.Qt.Key_Backspace - ): - for i in range(len(self.rasterList)): - if self.rasterList[i] != None: - if self.rasterList[i]["graphicsItem"].isSelected(): - try: - sceneReq = db_lib.getRequestByID(self.rasterList[i]["uid"]) - if sceneReq != None: - self.selectedSampleID = sceneReq["sample"] - db_lib.deleteRequest(sceneReq)["uid"] - except AttributeError: - pass - self.scene.removeItem(self.rasterList[i]["graphicsItem"]) - self.rasterList[i] = None - self.treeChanged_pv.put(1) - for i in range(len(self.centeringMarksList)): - if self.centeringMarksList[i] != None: - if self.centeringMarksList[i]["graphicsItem"].isSelected(): - self.scene.removeItem( - self.centeringMarksList[i]["graphicsItem"] - ) - self.centeringMarksList[i] = None - - def pixelSelect(self, event): - super(QtWidgets.QGraphicsPixmapItem, self.pixmap_item).mousePressEvent(event) - x_click = float(event.pos().x()) - y_click = float(event.pos().y()) - penGreen = QtGui.QPen(QtCore.Qt.green) - penRed = QtGui.QPen(QtCore.Qt.red) - if self.vidActionDefineCenterRadio.isChecked(): - self.vidActionC2CRadio.setChecked( - True - ) # because it's easy to forget defineCenter is on - if self.zoom4Radio.isChecked(): - comm_s = ( - "changeImageCenterHighMag(" - + str(x_click) - + "," - + str(y_click) - + ",1)" - ) - elif self.zoom3Radio.isChecked(): - comm_s = ( - "changeImageCenterHighMag(" - + str(x_click) - + "," - + str(y_click) - + ",0)" - ) - if self.zoom2Radio.isChecked(): - comm_s = ( - "changeImageCenterLowMag(" - + str(x_click) - + "," - + str(y_click) - + ",1)" - ) - elif self.zoom1Radio.isChecked(): - comm_s = ( - "changeImageCenterLowMag(" - + str(x_click) - + "," - + str(y_click) - + ",0)" - ) - self.send_to_server(comm_s) - return - if self.vidActionRasterDefRadio.isChecked(): - self.click_positions.append(event.pos()) - self.polyPointItems.append( - self.scene.addEllipse(x_click, y_click, 4, 4, penRed) - ) - if len(self.click_positions) == 4: - self.drawInteractiveRasterCB() - return - fov = self.getCurrentFOV() - correctedC2C_x = self.getMD2BeamCenterX() + ( - x_click - (self.centerMarker.x() + self.centerMarkerCharOffsetX) - ) - correctedC2C_y = self.getMD2BeamCenterY() + ( - y_click - (self.centerMarker.y() + self.centerMarkerCharOffsetY) - ) - - current_viewangle = daq_utils.mag1ViewAngle - if self.zoom2Radio.isChecked(): - current_viewangle = daq_utils.mag2ViewAngle - elif self.zoom3Radio.isChecked(): - current_viewangle = daq_utils.mag3ViewAngle - elif self.zoom4Radio.isChecked(): - current_viewangle = daq_utils.mag4ViewAngle - - if self.threeClickCount > 0: # 3-click centering - self.threeClickCount = self.threeClickCount + 1 - if daq_utils.exporter_enabled: - correctedC2C_x = x_click + ((daq_utils.screenPixX/2) - (self.centerMarker.x() + self.centerMarkerCharOffsetX)) - correctedC2C_y = y_click + ((daq_utils.screenPixY/2) - (self.centerMarker.y() + self.centerMarkerCharOffsetY)) - lsdc_x = daq_utils.screenPixX - lsdc_y = daq_utils.screenPixY - md2_x = self.md2.center_pixel_x.get() * 2 - md2_y = self.md2.center_pixel_y.get() * 2 - scale_x = md2_x / lsdc_x - scale_y = md2_y / lsdc_y - correctedC2C_x = correctedC2C_x * scale_x - correctedC2C_y = correctedC2C_y * scale_y - self.md2.centring_click.put(f"{correctedC2C_x} {correctedC2C_y}") - if self.threeClickCount == 4: - self.threeClickCount = 0 - self.click3Button.setStyleSheet("background-color: None") - return - else: - comm_s = f'center_on_click({correctedC2C_x},{correctedC2C_y},{fov["x"]},{fov["y"]},source="screen",jog=90,viewangle={current_viewangle})' - else: - comm_s = f'center_on_click({correctedC2C_x},{correctedC2C_y},{fov["x"]},{fov["y"]},source="screen",maglevel=0,viewangle={current_viewangle})' - if not self.vidActionRasterExploreRadio.isChecked(): - self.aux_send_to_server(comm_s) - if self.threeClickCount == 4: - self.threeClickCount = 0 - self.click3Button.setStyleSheet("background-color: None") - return - - def editScreenParamsCB(self): - self.screenDefaultsDialog = ScreenDefaultsDialog(self) - self.screenDefaultsDialog.show() - - def editSelectedRequestsCB(self): - selmod = self.dewarTree.selectionModel() - selection = selmod.selection() - indexes = selection.indexes() - singleRequest = 1 - for i in range(len(indexes)): - item = self.dewarTree.model.itemFromIndex(indexes[i]) - itemData = str(item.data(32)) - itemDataType = str(item.data(33)) - if itemDataType == "request": - self.selectedSampleRequest = db_lib.getRequestByID(itemData) - self.editSampleRequestCB(singleRequest) - singleRequest = 0 - self.treeChanged_pv.put(1) - - def editSampleRequestCB(self, singleRequest): - colRequest = self.selectedSampleRequest - reqObj = colRequest["request_obj"] - if not self.validateAllFields(): - return - try: - reqObj["sweep_start"] = float(self.osc_start_ledit.text()) - reqObj["sweep_end"] = float(self.osc_end_ledit.text()) + float( - self.osc_start_ledit.text() - ) - reqObj["img_width"] = float(self.osc_range_ledit.text()) - reqObj["exposure_time"] = float(self.exp_time_ledit.text()) - reqObj["detDist"] = float(self.detDistMotorEntry.getEntry().text()) - reqObj["resolution"] = float(self.resolution_ledit.text()) - if ( - singleRequest == 1 - ): # a touch kludgy, but I want to be able to edit parameters for multiple requests w/o screwing the data loc info - reqObj["file_prefix"] = str(self.dataPathGB.prefix_ledit.text()) - reqObj["basePath"] = getBlConfig("visitDirectory") - reqObj["directory"] = str(self.dataPathGB.dataPath_ledit.text()) - reqObj["file_number_start"] = int( - self.dataPathGB.file_numstart_ledit.text() - ) - reqObj["attenuation"] = float(self.transmission_ledit.text()) - reqObj["slit_width"] = float(self.beamWidth_ledit.text()) - reqObj["slit_height"] = float(self.beamHeight_ledit.text()) - reqObj["energy"] = float(self.energy_ledit.text()) - wave = daq_utils.energy2wave(float(self.energy_ledit.text()), digits=6) - reqObj["wavelength"] = wave - except ValueError: - message = "Please ensure that all boxes that expect numerical values have numbers in them" - logger.error(message) - self.popupServerMessage(f"{message}") - return - reqObj["fastDP"] = ( - self.staffScreenDialog.fastDPCheckBox.isChecked() - or self.fastEPCheckBox.isChecked() - or self.dimpleCheckBox.isChecked() - ) - reqObj["fastEP"] = self.fastEPCheckBox.isChecked() - reqObj["dimple"] = self.dimpleCheckBox.isChecked() - reqObj["xia2"] = self.xia2CheckBox.isChecked() - reqObj["protocol"] = str(self.protoComboBox.currentText()) - if reqObj["protocol"] == "vector" or reqObj["protocol"] == "stepVector": - reqObj["vectorParams"]["fpp"] = int(self.vectorFPP_ledit.text()) - colRequest["request_obj"] = reqObj - db_lib.updateRequest(colRequest) - self.treeChanged_pv.put(1) - - def addRequestsToAllSelectedCB(self): - if ( - self.protoComboBox.currentText() == "raster" - or self.protoComboBox.currentText() == "stepRaster" - ): # it confused people when they didn't need to add rasters explicitly - return - selmod = self.dewarTree.selectionModel() - selection = selmod.selection() - indexes = selection.indexes() - try: - progressInc = 100.0 / float(len(indexes)) - except ZeroDivisionError: - self.popupServerMessage("Select a sample to perform the request on!") - return - self.progressDialog.setWindowTitle("Creating Requests") - self.progressDialog.show() - if ( - getBlConfig("queueCollect") == 1 - ): # If queue collect is ON only consider selected samples/requests and add a request to each - samplesConsidered = set() - for i in range(len(indexes)): - self.progressDialog.setValue(int((i + 1) * progressInc)) - item = self.dewarTree.model.itemFromIndex(indexes[i]) - itemData = str(item.data(32)) - itemDataType = str(item.data(33)) - if itemDataType == "sample": - self.selectedSampleID = itemData - elif itemDataType == "request": - selectedSampleRequest = db_lib.getRequestByID(item.data(32)) - self.selectedSampleID = selectedSampleRequest["sample"] - - # If a request is already added to the sample, move on - if self.selectedSampleID in samplesConsidered: - continue - - try: - self.selectedSampleRequest = daq_utils.createDefaultRequest( - self.selectedSampleID - ) - except KeyError: - self.popupServerMessage("Please select a sample!") - self.progressDialog.close() - return - if len(indexes) > 1: - self.dataPathGB.setFilePrefix_ledit( - str(self.selectedSampleRequest["request_obj"]["file_prefix"]) - ) - self.dataPathGB.setDataPath_ledit( - str(self.selectedSampleRequest["request_obj"]["directory"]) - ) - self.EScanDataPathGB.setFilePrefix_ledit( - str(self.selectedSampleRequest["request_obj"]["file_prefix"]) - ) - self.EScanDataPathGB.setDataPath_ledit( - str(self.selectedSampleRequest["request_obj"]["directory"]) - ) - if itemDataType != "container": - self.addSampleRequestCB(selectedSampleID=self.selectedSampleID) - samplesConsidered.add(self.selectedSampleID) - else: # If queue collect is off does not matter how many requests you select only one will be added to current pin - self.selectedSampleID = self.mountedPin_pv.get() - self.selectedSampleRequest = daq_utils.createDefaultRequest( - self.selectedSampleID - ) - self.dataPathGB.setFilePrefix_ledit( - str(self.selectedSampleRequest["request_obj"]["file_prefix"]) - ) - self.dataPathGB.setDataPath_ledit( - str(self.selectedSampleRequest["request_obj"]["directory"]) - ) - self.EScanDataPathGB.setFilePrefix_ledit( - str(self.selectedSampleRequest["request_obj"]["file_prefix"]) - ) - self.EScanDataPathGB.setDataPath_ledit( - str(self.selectedSampleRequest["request_obj"]["directory"]) - ) - self.addSampleRequestCB(selectedSampleID=self.selectedSampleID) - - self.progressDialog.close() - self.treeChanged_pv.put(1) - - def addSampleRequestCB(self, rasterDef=None, selectedSampleID=None): - if self.selectedSampleID != None: - try: - sample = db_lib.getSampleByID(self.selectedSampleID) - propNum = sample["proposalID"] - except KeyError: - propNum = 999999 - if propNum == None: - propNum = 999999 - if propNum != daq_utils.getProposalID(): - logger.info("setting proposal in add request") - daq_utils.setProposalID(propNum, createVisit=True) - - if getBlConfig("queueCollect") == 0: - if self.mountedPin_pv.get() != self.selectedSampleID: - self.selectedSampleID = self.mountedPin_pv.get() - - if not self.validateAllFields(): - return - # skinner, not pretty below the way stuff is duplicated. - try: - if ( - float(self.osc_end_ledit.text()) < float(self.osc_range_ledit.text()) - ) and str(self.protoComboBox.currentText()) != "eScan": - self.popupServerMessage("Osc range less than Osc width") - return - except ValueError: - message = ( - "Please ensure oscillation end and oscillation range are valid numbers" - ) - logger.error(message) - self.popupServerMessage(f"{message}") - return - - if self.periodicTable.isVisible(): - if self.periodicTable.eltCurrent != None: - symbol = self.periodicTable.eltCurrent.symbol - targetEdge = element_info[symbol][2] - if daq_utils.beamline == "fmx": - mcaRoiLo = element_info[symbol][4] - mcaRoiHi = element_info[symbol][5] - else: - mcaRoiLo = self.XRFInfoDict[symbol] - 25 - mcaRoiHi = self.XRFInfoDict[symbol] + 25 - targetEnergy = Elements.Element[symbol]["binding"][targetEdge] - currentEnergy = float(self.energy_ledit.text()) - energyDiff = abs( - currentEnergy - targetEnergy * 1000 - ) # targetEnergy is in keV, LSDC energy is in eV - if energyDiff >= 20: - message = f"Please choose an element with an edge closer to {currentEnergy}. The energy difference to {symbol} is {energyDiff:.1f}" - logger.error(message) - self.popupServerMessage(f"{message}") - return - colRequest = daq_utils.createDefaultRequest(self.selectedSampleID) - sampleName = str(db_lib.getSampleNamebyID(colRequest["sample"])) - runNum = db_lib.incrementSampleRequestCount(colRequest["sample"]) - ( - puckPosition, - samplePositionInContainer, - containerID, - ) = db_lib.getCoordsfromSampleID( - daq_utils.beamline, colRequest["sample"] - ) - reqObj = get_request_object_escan( - colRequest["request_obj"], - self.periodicTable.eltCurrent.symbol, - runNum, - self.EScanDataPathGB.prefix_ledit.text(), - self.EScanDataPathGB.base_path_ledit.text(), - sampleName, - containerID, - samplePositionInContainer, - self.EScanDataPathGB.file_numstart_ledit.text(), - self.exp_time_ledit.text(), - targetEnergy, - self.escan_steps_ledit.text(), - self.escan_stepsize_ledit.text(), - ) - reqObj["detDist"] = float(self.detDistMotorEntry.getEntry().text()) - reqObj["attenuation"] = float(self.transmission_ledit.text()) - reqObj["mcaRoiLo"] = mcaRoiLo - reqObj["mcaRoiHi"] = mcaRoiHi - - colRequest["request_obj"] = reqObj - newSampleRequestID = db_lib.addRequesttoSample( - self.selectedSampleID, - reqObj["protocol"], - daq_utils.owner, - reqObj, - priority=5000, - proposalID=daq_utils.getProposalID(), - ) - # attempt here to select a newly created request. - self.SelectedItemData = newSampleRequestID - - if ( - selectedSampleID == None - ): # this is a temp kludge to see if this is called from addAll - self.treeChanged_pv.put(1) - else: - logger.info("choose an element and try again") - return - - # I don't like the code duplication, but one case is the mounted sample and selected centerings - so it's in a loop for multiple reqs, the other requires autocenter. - if (self.mountedPin_pv.get() == self.selectedSampleID) and ( - len(self.centeringMarksList) != 0 - ): - selectedCenteringFound = 0 - for i in range(len(self.centeringMarksList)): - if self.centeringMarksList[i]["graphicsItem"].isSelected(): - selectedCenteringFound = 1 - colRequest = daq_utils.createDefaultRequest(self.selectedSampleID) - sampleName = str(db_lib.getSampleNamebyID(colRequest["sample"])) - runNum = db_lib.incrementSampleRequestCount(colRequest["sample"]) - ( - puckPosition, - samplePositionInContainer, - containerID, - ) = db_lib.getCoordsfromSampleID( - daq_utils.beamline, colRequest["sample"] - ) - reqObj = colRequest["request_obj"] - try: - reqObj["runNum"] = runNum - reqObj["sweep_start"] = float(self.osc_start_ledit.text()) - reqObj["sweep_end"] = float(self.osc_end_ledit.text()) + float( - self.osc_start_ledit.text() - ) - reqObj["img_width"] = float(self.osc_range_ledit.text()) - setBlConfig( - "screen_default_width", float(self.osc_range_ledit.text()) - ) - setBlConfig( - "screen_default_time", float(self.exp_time_ledit.text()) - ) - setBlConfig("stdTrans", float(self.transmission_ledit.text())) - setBlConfig( - "screen_default_dist", - float(self.detDistMotorEntry.getEntry().text()), - ) - reqObj["exposure_time"] = float(self.exp_time_ledit.text()) - reqObj["resolution"] = float(self.resolution_ledit.text()) - reqObj["file_prefix"] = str( - self.dataPathGB.prefix_ledit.text() + "_C" + str(i + 1) - ) - reqObj["basePath"] = getBlConfig("visitDirectory") - reqObj["directory"] = ( - getBlConfig("visitDirectory") - + "/" - + str(daq_utils.getVisitName()) - + "/" - + sampleName - + "/" - + str(runNum) - + "/" - + db_lib.getContainerNameByID(containerID) - + "_" - + str(samplePositionInContainer + 1) - + "/" - ) - reqObj["file_number_start"] = int( - self.dataPathGB.file_numstart_ledit.text() - ) - reqObj["attenuation"] = float(self.transmission_ledit.text()) - reqObj["slit_width"] = float(self.beamWidth_ledit.text()) - reqObj["slit_height"] = float(self.beamHeight_ledit.text()) - reqObj["energy"] = float(self.energy_ledit.text()) - wave = daq_utils.energy2wave( - float(self.energy_ledit.text()), digits=6 - ) - reqObj["wavelength"] = wave - except ValueError: - message = "Please ensure that all boxes that expect numerical values have numbers in them" - logger.error(message) - self.popupServerMessage(f"{message}") - return - reqObj["detDist"] = float(self.detDistMotorEntry.getEntry().text()) - reqObj["protocol"] = str(self.protoComboBox.currentText()) - reqObj["pos_x"] = float( - self.centeringMarksList[i]["sampCoords"]["x"] - ) - reqObj["pos_y"] = float( - self.centeringMarksList[i]["sampCoords"]["y"] - ) - reqObj["pos_z"] = float( - self.centeringMarksList[i]["sampCoords"]["z"] - ) - reqObj["fastDP"] = ( - self.staffScreenDialog.fastDPCheckBox.isChecked() - or self.fastEPCheckBox.isChecked() - or self.dimpleCheckBox.isChecked() - ) - reqObj["fastEP"] = self.fastEPCheckBox.isChecked() - reqObj["dimple"] = self.dimpleCheckBox.isChecked() - reqObj["xia2"] = self.xia2CheckBox.isChecked() - if ( - reqObj["protocol"] == "characterize" - or reqObj["protocol"] == "ednaCol" - ): - characterizationParams = { - "aimed_completeness": float( - self.characterizeCompletenessEdit.text() - ), - "aimed_multiplicity": str( - self.characterizeMultiplicityEdit.text() - ), - "aimed_resolution": float(self.characterizeResoEdit.text()), - "aimed_ISig": float(self.characterizeISIGEdit.text()), - } - reqObj["characterizationParams"] = characterizationParams - colRequest["request_obj"] = reqObj - newSampleRequestID = db_lib.addRequesttoSample( - self.selectedSampleID, - reqObj["protocol"], - daq_utils.owner, - reqObj, - priority=5000, - proposalID=daq_utils.getProposalID(), - ) - # attempt here to select a newly created request. - self.SelectedItemData = newSampleRequestID - if selectedCenteringFound == 0: - message = QtWidgets.QErrorMessage(self) - message.setModal(False) - message.showMessage("You need to select a centering.") - else: # autocenter or interactive - colRequest = self.selectedSampleRequest - try: - sampleName = str(db_lib.getSampleNamebyID(colRequest["sample"])) - except KeyError: - logger.error("no sample selected") - self.popupServerMessage("no sample selected") - return - ( - puckPosition, - samplePositionInContainer, - containerID, - ) = db_lib.getCoordsfromSampleID(daq_utils.beamline, colRequest["sample"]) - runNum = db_lib.incrementSampleRequestCount(colRequest["sample"]) - reqObj = colRequest["request_obj"] - centeringOption = str(self.centeringComboBox.currentText()) - reqObj["centeringOption"] = centeringOption - if ( - centeringOption == "Interactive" - and self.mountedPin_pv.get() == self.selectedSampleID - ) or centeringOption == "Testing": # user centered manually - reqObj["pos_x"] = float(self.gon.x.val()) - reqObj["pos_y"] = float(self.gon.y.val()) - reqObj["pos_z"] = float(self.gon.z.val()) - reqObj["runNum"] = runNum - try: - reqObj["sweep_start"] = float(self.osc_start_ledit.text()) - reqObj["sweep_end"] = float(self.osc_end_ledit.text()) + float( - self.osc_start_ledit.text() - ) - reqObj["img_width"] = float(self.osc_range_ledit.text()) - reqObj["exposure_time"] = float(self.exp_time_ledit.text()) - if rasterDef == None and reqObj["protocol"] != "burn": - setBlConfig( - "screen_default_width", float(self.osc_range_ledit.text()) - ) - setBlConfig( - "screen_default_time", float(self.exp_time_ledit.text()) - ) - setBlConfig("stdTrans", float(self.transmission_ledit.text())) - setBlConfig( - "screen_default_dist", - float(self.detDistMotorEntry.getEntry().text()), - ) - reqObj["resolution"] = float(self.resolution_ledit.text()) - reqObj["directory"] = ( - getBlConfig("visitDirectory") - + "/" - + str(daq_utils.getVisitName()) - + "/" - + str(self.dataPathGB.prefix_ledit.text()) - + "/" - + str(runNum) - + "/" - + db_lib.getContainerNameByID(containerID) - + "_" - + str(samplePositionInContainer + 1) - + "/" - ) - reqObj["basePath"] = getBlConfig("visitDirectory") - reqObj["file_prefix"] = str(self.dataPathGB.prefix_ledit.text()) - reqObj["file_number_start"] = int( - self.dataPathGB.file_numstart_ledit.text() - ) - if abs(reqObj["sweep_end"] - reqObj["sweep_start"]) < 5.0: - reqObj["fastDP"] = False - reqObj["fastEP"] = False - reqObj["dimple"] = False - else: - reqObj["fastDP"] = ( - self.staffScreenDialog.fastDPCheckBox.isChecked() - or self.fastEPCheckBox.isChecked() - or self.dimpleCheckBox.isChecked() - ) - reqObj["fastEP"] = self.fastEPCheckBox.isChecked() - reqObj["dimple"] = self.dimpleCheckBox.isChecked() - reqObj["xia2"] = self.xia2CheckBox.isChecked() - reqObj["attenuation"] = float(self.transmission_ledit.text()) - reqObj["slit_width"] = float(self.beamWidth_ledit.text()) - reqObj["slit_height"] = float(self.beamHeight_ledit.text()) - reqObj["energy"] = float(self.energy_ledit.text()) - except ValueError: - message = "Please ensure that all boxes that expect numerical values have numbers in them" - logger.error(message) - self.popupServerMessage(f"{message}") - return - try: - wave = daq_utils.energy2wave(float(self.energy_ledit.text()), digits=6) - except ValueError: - wave = 1.1 - - reqObj["wavelength"] = wave - reqObj["protocol"] = str(self.protoComboBox.currentText()) - try: - reqObj["detDist"] = float(self.detDistMotorEntry.getEntry().text()) - except ValueError: - new_distance = 502.0 - logger.error("set dist to %s in exception handler 1" % new_distance) - reqObj["detDist"] = new_distance - if reqObj["protocol"] == "multiCol" or reqObj["protocol"] == "multiColQ": - reqObj["gridStep"] = float(self.rasterStepEdit.text()) - reqObj["diffCutoff"] = float(self.multiColCutoffEdit.text()) - if reqObj["protocol"] == "rasterScreen": - reqObj["gridStep"] = float(self.rasterStepEdit.text()) - if rasterDef != None: - reqObj["rasterDef"] = rasterDef - reqObj["gridStep"] = float(self.rasterStepEdit.text()) - if reqObj["protocol"] == "characterize" or reqObj["protocol"] == "ednaCol": - characterizationParams = { - "aimed_completeness": float( - self.characterizeCompletenessEdit.text() - ), - "aimed_multiplicity": str(self.characterizeMultiplicityEdit.text()), - "aimed_resolution": float(self.characterizeResoEdit.text()), - "aimed_ISig": float(self.characterizeISIGEdit.text()), - } - reqObj["characterizationParams"] = characterizationParams - if reqObj["protocol"] == "vector" or reqObj["protocol"] == "stepVector": - if float(self.osc_end_ledit.text()) < 5.0: - self.popupServerMessage( - "Vector oscillation must be at least 5.0 degrees." - ) - return - selectedCenteringFound = 1 - try: - x_vec, y_vec, z_vec, trans_total = self.updateVectorLengthAndSpeed() - framesPerPoint = int(self.vectorFPP_ledit.text()) - vectorParams = { - "vecStart": self.vectorStart["coords"], - "vecEnd": self.vectorEnd["coords"], - "x_vec": x_vec, - "y_vec": y_vec, - "z_vec": z_vec, - "trans_total": trans_total, - "fpp": framesPerPoint, - } - reqObj["vectorParams"] = vectorParams - except Exception as e: - if self.vectorStart == None: - self.popupServerMessage("Vector start must be defined.") - return - elif self.vectorEnd == None: - self.popupServerMessage("Vector end must be defined.") - return - logger.error("Exception while getting vector parameters: %s" % e) - pass - colRequest["request_obj"] = reqObj - newSampleRequestID = db_lib.addRequesttoSample( - self.selectedSampleID, - reqObj["protocol"], - daq_utils.owner, - reqObj, - priority=5000, - proposalID=daq_utils.getProposalID(), - ) - # attempt here to select a newly created request. - self.SelectedItemData = newSampleRequestID - newSampleRequest = db_lib.getRequestByID(newSampleRequestID) - if rasterDef != None: - self.rasterDefList.append(newSampleRequest) - self.drawPolyRaster(newSampleRequest) - if ( - selectedSampleID == None - ): # this is a temp kludge to see if this is called from addAll - self.treeChanged_pv.put(1) - - def cloneRequestCB(self): - self.eraseCB() - colRequest = self.selectedSampleRequest - reqObj = colRequest["request_obj"] - if "rasterDef" in reqObj: - self.addSampleRequestCB(reqObj["rasterDef"]) - - def collectQueueCB(self): - currentRequest = db_lib.popNextRequest(daq_utils.beamline) - if currentRequest == {}: - self.addRequestsToAllSelectedCB() - logger.info("running queue") - self.send_to_server("runDCQueue()") - - def warmupGripperCB(self): - self.send_to_server("warmupGripper()") - - def dryGripperCB(self): - self.send_to_server("dryGripper()") - - def enableTScreenGripperCB(self): - self.send_to_server("enableDewarTscreen()") - - def parkGripperCB(self): - self.send_to_server("parkGripper()") - - def restartServerCB(self): - if self.controlEnabled(): - msg = "Desperation move. Are you sure?" - #self.timerSample.stop() - reply = QtWidgets.QMessageBox.question( - self, - "Message", - msg, - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No, - ) - #self.timerSample.start(SAMPLE_TIMER_DELAY) - if reply == QtWidgets.QMessageBox.Yes: - if daq_utils.beamline == "fmx" or daq_utils.beamline == "amx": - restart_pv = PV(daq_utils.beamlineComm + "RestartServerSignal") - restart_pv.put(not (restart_pv.get())) - else: - logger.error("Not restarting server - unknown beamline") - else: - self.popupServerMessage("You don't have control") - - def openPhotonShutterCB(self): - self.photonShutterOpen_pv.put(1) - - def popUserScreenCB(self): - if self.controlEnabled(): - self.userScreenDialog.show() - else: - self.popupServerMessage("You don't have control") - - def parkRobotCB(self): - self.send_to_server("parkRobot()") - - def closePhotonShutterCB(self): - self.photonShutterClose_pv.put(1) - - def removePuckCB(self): - #self.timerSample.stop() - dewarPos, ok = DewarDialog.getDewarPos(parent=self, action="remove") - #self.timerSample.start(SAMPLE_TIMER_DELAY) - - def transform_vector_coords(self, prev_coords, current_raw_coords): - """Updates y and z co-ordinates of vector points when they are moved - - This function tweaks the y and z co-ordinates such that when a vector start or - end point is adjusted in the 2-D plane of the screen, it maintains the points' location - in the 3rd dimension perpendicular to the screen - - Args: - prev_coords: Dictionary with x,y and z co-ordinates of the previous location of the sample - current_raw_coords: Dictionary with x, y and z co-ordinates of the sample derived from the goniometer - PVs - omega: Omega of the Goniometer (usually RBV) - - Returns: - A dictionary mapping x, y and z to tweaked coordinates - """ - - # Transform z from prev point and y from current point to lab coordinate system - _, _, zLabPrev, _ = daq_utils.gonio2lab( - prev_coords["x"], - prev_coords["y"], - prev_coords["z"], - current_raw_coords["omega"], - ) - _, yLabCurrent, _, _ = daq_utils.gonio2lab( - current_raw_coords["x"], - current_raw_coords["y"], - current_raw_coords["z"], - current_raw_coords["omega"], - ) - - # Take y co-ordinate from current point and z-coordinate from prev point and transform back to gonio co-ordinates - _, yTweakedCurrent, zTweakedCurrent, _ = daq_utils.lab2gonio( - prev_coords["x"], yLabCurrent, zLabPrev, current_raw_coords["omega"] - ) - return { - "x": current_raw_coords["x"], - "y": yTweakedCurrent, - "z": zTweakedCurrent, - } - - def getVectorObject( - self, prevVectorPoint=None, gonioCoords=None, pen=None, brush=None - ): - """Creates and returns a vector start or end point - - Places a start or end vector marker wherever the crosshair is located in - the sample camera view and returns a dictionary of metadata related to that point - - Args: - prevVectorPoint: Dictionary of metadata related to a point being adjusted. For example, - a previously placed vectorStart point is moved, its old position is used to determine - its new co-ordinates in 3D space - gonioCoords: Dictionary of gonio coordinates. If not provided will retrieve current PV values - pen: QPen object that defines the color of the point's outline - brush: QBrush object that defines the color of the point's fill color - Returns: - A dict mapping the following keys - "coords": A dictionary of tweaked x, y and z positions of the Goniometer - "raw_coords": A dictionary of x, y, z co-ordinates obtained from the Goniometer PVs - "graphicsitem": Qt object referring to the marker on the sample camera - "centerCursorX" and "centerCursorY": Location of the center marker when this marker was placed - """ - if not pen: - pen = QtGui.QPen(QtCore.Qt.blue) - if not brush: - brush = QtGui.QBrush(QtCore.Qt.blue) - markWidth = 10 - # TODO: Place vecMarker in such a way that it matches any arbitrary gonioCoords given to this function - # currently vecMarker will be placed at the center of the sample cam - vecMarker = self.scene.addEllipse( - self.centerMarker.x() - - (markWidth / 2.0) - - 1 - + self.centerMarkerCharOffsetX, - self.centerMarker.y() - - (markWidth / 2.0) - - 1 - + self.centerMarkerCharOffsetY, - markWidth, - markWidth, - pen, - brush, - ) - if not gonioCoords: - gonioCoords = { - "x": self.gon.x.val(), - "y": self.gon.y.val(), - "z": self.gon.z.val(), - "finex": self.gon.cx.val(), - "finey": self.gon.cy.val(), - "omega": self.gon.omega.val(), - } - #if prevVectorPoint: - # vectorCoords = self.transform_vector_coords( - # prevVectorPoint["coords"], gonioCoords - # ) - #else: - vectorCoords = { - k: v for k, v in gonioCoords.items() if k in ["x", "y", "z", "finex", "finey"] - } - logger.info("vector coords: %s" % vectorCoords) - return { - "coords": vectorCoords, - "gonioCoords": gonioCoords, - "graphicsitem": vecMarker, - "centerCursorX": self.centerMarker.x(), - "centerCursorY": self.centerMarker.y(), - } - - def setVectorPointCB(self, pointName): - """Callback function to update a vector point - - Callback to remove or add the appropriate vector start or end point on the sample camera. - Calls getVectorObject to generate metadata related to this point. Draws a vector line if - both start and end is defined - - Args: - point: Point to be placed (Either vectorStart or vectorEnd) - """ - point = getattr(self, pointName) - if point: - self.scene.removeItem(point["graphicsitem"]) - if self.vecLine: - self.scene.removeItem(self.vecLine) - if pointName == "vectorEnd": - brush = QtGui.QBrush(QtCore.Qt.red) - else: - brush = QtGui.QBrush(QtCore.Qt.blue) - point = self.getVectorObject(prevVectorPoint=point, brush=brush) - setattr(self, pointName, point) - if self.vectorStart and self.vectorEnd: - self.drawVector() - - def drawVector(self): - pen = QtGui.QPen(QtCore.Qt.blue) - try: - self.updateVectorLengthAndSpeed() - except: - pass - self.protoVectorRadio.setChecked(True) - self.vecLine = self.scene.addLine( - self.centerMarker.x() - + self.vectorStart["graphicsitem"].x() - + self.centerMarkerCharOffsetX, - self.centerMarker.y() - + self.vectorStart["graphicsitem"].y() - + self.centerMarkerCharOffsetY, - self.centerMarker.x() - + self.vectorEnd["graphicsitem"].x() - + self.centerMarkerCharOffsetX, - self.centerMarker.y() - + self.vectorEnd["graphicsitem"].y() - + self.centerMarkerCharOffsetY, - pen, - ) - self.vecLine.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True) - - def clearVectorCB(self): - if self.vectorStart: - self.scene.removeItem(self.vectorStart["graphicsitem"]) - self.vectorStart = None - if self.vectorEnd: - self.scene.removeItem(self.vectorEnd["graphicsitem"]) - self.vectorEnd = None - self.vecLenLabelOutput.setText("---") - self.vecSpeedLabelOutput.setText("---") - if self.vecLine: - self.scene.removeItem(self.vecLine) - self.vecLine = None - - def puckToDewarCB(self): - while 1: - #self.timerSample.stop() - puckName, ok = PuckDialog.getPuckName() - #self.timerSample.start(SAMPLE_TIMER_DELAY) - if ok: - #self.timerSample.stop() - dewarPos, ok = DewarDialog.getDewarPos(parent=self, action="add") - #self.timerSample.start(SAMPLE_TIMER_DELAY) - if ok and dewarPos is not None and puckName is not None: - ipos = int(dewarPos) + 1 - db_lib.insertIntoContainer( - daq_utils.primaryDewarName, - daq_utils.beamline, - ipos, - db_lib.getContainerIDbyName(puckName, daq_utils.owner), - ) - self.treeChanged_pv.put(1) - else: - break - - def stopRunCB(self): - logger.info("stopping collection") - self.aux_send_to_server("stopDCQueue(1)") - - def stopQueueCB(self): - logger.info("stopping queue") - if self.pauseQueueButton.text() == "Continue": - self.aux_send_to_server("continue_data_collection()") - else: - self.aux_send_to_server("stopDCQueue(2)") - - def mountSampleCB(self): - if getBlConfig("mountEnabled") == 0: - self.popupServerMessage("Mounting disabled!! Call staff!") - return - logger.info("mount selected sample") - self.eraseCB() - if ( - self.dewarTree.getSelectedSample() - ): # If sample ID is not found check the dewartree directly - self.selectedSampleID = self.dewarTree.getSelectedSample() - else: # No sample ID found, do nothing - logger.info("No sample selected, cannot mount") - return - self.send_to_server('mountSample("' + str(self.selectedSampleID) + '")') - self.zoom2Radio.setChecked(True) - self.zoomLevelToggledCB("Zoom2") - self.protoComboBox.setCurrentIndex(self.protoComboBox.findText(str("standard"))) - self.protoComboActivatedCB("standard") - - def unmountSampleCB(self): - logger.info("unmount sample") - self.send_to_server("unmountSample()") - - def refreshCollectionParams(self, selectedSampleRequest, validate_hdf5=True): - reqObj = selectedSampleRequest["request_obj"] - self.protoComboBox.setCurrentIndex( - self.protoComboBox.findText(str(reqObj["protocol"])) - ) - protocol = str(reqObj["protocol"]) - if protocol == "raster": - self.protoRasterRadio.setChecked(True) - elif protocol == "standard": - self.protoStandardRadio.setChecked(True) - elif protocol == "vector": - self.protoVectorRadio.setChecked(True) - else: - self.protoOtherRadio.setChecked(True) - - logger.info("osc range") - self.setGuiValues( - { - "osc_start": reqObj["sweep_start"], - "osc_end": reqObj["sweep_end"] - reqObj["sweep_start"], - "osc_range": reqObj["img_width"], - "exp_time": reqObj["exposure_time"], - "resolution": reqObj["resolution"], - "transmission": reqObj["attenuation"], - } - ) - self.dataPathGB.setFileNumstart_ledit(str(reqObj["file_number_start"])) - self.beamWidth_ledit.setText(str(reqObj["slit_width"])) - self.beamHeight_ledit.setText(str(reqObj["slit_height"])) - if "fastDP" in reqObj: - self.staffScreenDialog.fastDPCheckBox.setChecked( - (reqObj["fastDP"] or reqObj["fastEP"] or reqObj["dimple"]) - ) - if "fastEP" in reqObj: - self.fastEPCheckBox.setChecked(reqObj["fastEP"]) - if "dimple" in reqObj: - self.dimpleCheckBox.setChecked(reqObj["dimple"]) - if "xia2" in reqObj: - self.xia2CheckBox.setChecked(reqObj["xia2"]) - reqObj["energy"] = float(self.energy_ledit.text()) - self.energy_ledit.setText(str(reqObj["energy"])) - energy_s = str(daq_utils.wave2energy(reqObj["wavelength"], digits=6)) - dist_s = str(reqObj["detDist"]) - self.detDistMotorEntry.getEntry().setText(str(dist_s)) - self.dataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) - self.dataPathGB.setBasePath_ledit(str(reqObj["basePath"])) - self.dataPathGB.setDataPath_ledit(str(reqObj["directory"])) - if ( - str(reqObj["protocol"]) == "characterize" - or str(reqObj["protocol"]) == "ednaCol" - ): - prefix_long = ( - str(reqObj["directory"]) + "/ref-" + str(reqObj["file_prefix"]) - ) - else: - prefix_long = str(reqObj["directory"]) + "/" + str(reqObj["file_prefix"]) - fnumstart = reqObj["file_number_start"] - - if ( - str(reqObj["protocol"]) == "characterize" - or str(reqObj["protocol"]) == "ednaCol" - or str(reqObj["protocol"]) == "standard" - or str(reqObj["protocol"]) == "vector" - ): - if "priority" in selectedSampleRequest: - if ( - selectedSampleRequest["priority"] < 0 - and self.staffScreenDialog.albulaDispCheckBox.isChecked() - ): - firstFilename = daq_utils.create_filename(prefix_long, fnumstart) - if validate_hdf5: - if albulaUtils.validate_master_HDF5_file(firstFilename): - albulaUtils.albulaDispFile(firstFilename) - else: - QtWidgets.QMessageBox.information( - self, - "Error", - f"Master HDF5 file {firstFilename} could not be validated", - QtWidgets.QMessageBox.Ok, - ) - self.rasterStepEdit.setText(str(reqObj["gridStep"])) - if reqObj["gridStep"] == self.rasterStepDefs["Coarse"]: - self.rasterGrainCoarseRadio.setChecked(True) - elif reqObj["gridStep"] == self.rasterStepDefs["Fine"]: - self.rasterGrainFineRadio.setChecked(True) - elif reqObj["gridStep"] == self.rasterStepDefs["VFine"]: - self.rasterGrainVFineRadio.setChecked(True) - else: - self.rasterGrainCustomRadio.setChecked(True) - rasterStep = int(reqObj["gridStep"]) - if not self.hideRastersCheckBox.isChecked() and ( - reqObj["protocol"] in ("raster", "stepRaster", "multiCol") - ): - if not self.rasterIsDrawn(selectedSampleRequest): - self.drawPolyRaster(selectedSampleRequest) - self.fillPolyRaster(selectedSampleRequest) - - if ( - str(self.govStateMessagePV.get(as_string=True)) == "state SA" - and self.controlEnabled() # Move only in SA (Any other way for GUI to detect governor state?) - and self.selectedSampleRequest["sample"] # with control enabled - == self.mountedPin_pv.get() - ): # And the sample of the selected request is mounted - self.processSampMove(self.gon.x.val(), "x") - self.processSampMove(self.gon.y.val(), "y") - self.processSampMove(self.gon.z.val(), "z") - if ( - abs( - selectedSampleRequest["request_obj"]["rasterDef"]["omega"] - - self.gon.omega.val() - ) - > 5.0 - ): - comm_s = ( - 'mvaDescriptor("omega",' - + str( - selectedSampleRequest["request_obj"]["rasterDef"]["omega"] - ) - + ")" - ) - self.send_to_server(comm_s) - if str(reqObj["protocol"]) == "eScan": - try: - self.escan_steps_ledit.setText(str(reqObj["steps"])) - self.escan_stepsize_ledit.setText(str(reqObj["stepsize"])) - self.EScanDataPathGB.setBasePath_ledit(reqObj["basePath"]) - self.EScanDataPathGB.setDataPath_ledit(reqObj["directory"]) - self.EScanDataPathGB.setFileNumstart_ledit( - str(reqObj["file_number_start"]) - ) - self.EScanDataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) - self.periodicTable.elementClicked(reqObj["element"]) - except KeyError: - pass - elif ( - str(reqObj["protocol"]) == "characterize" - or str(reqObj["protocol"]) == "ednaCol" - ): - characterizationParams = reqObj["characterizationParams"] - self.characterizeCompletenessEdit.setText( - str(characterizationParams["aimed_completeness"]) - ) - self.characterizeISIGEdit.setText(str(characterizationParams["aimed_ISig"])) - self.characterizeResoEdit.setText( - str(characterizationParams["aimed_resolution"]) - ) - self.characterizeMultiplicityEdit.setText( - str(characterizationParams["aimed_multiplicity"]) - ) - else: # for now, erase the rasters if a non-raster is selected, need to rationalize later - pass - self.showProtParams() - - def row_clicked( - self, index - ): # I need "index" here? seems like I get it from selmod, but sometimes is passed - selmod = self.dewarTree.selectionModel() - if not selmod: - return - selection = selmod.selection() - indexes = selection.indexes() - if len(indexes) == 0: - return - i = 0 - item = self.dewarTree.model.itemFromIndex(indexes[i]) - parent = indexes[i].parent() - try: - puck_name = parent.data() - except AttributeError as e: - logger.error("attribute error in row_clicked: %s", e) - return - itemData = str(item.data(32)) - itemDataType = str(item.data(33)) - self.SelectedItemData = itemData # an attempt to know what is selected and preserve it when refreshing the tree - if itemData == "": - logger.info("nothing there") - return - elif itemDataType == "container": - logger.info("I'm a puck") - return - elif itemDataType == "sample": - self.selectedSampleID = itemData - sample = db_lib.getSampleByID(self.selectedSampleID) - owner = sample["owner"] - sample_name = db_lib.getSampleNamebyID(self.selectedSampleID) - logger.info("sample in pos " + str(itemData)) - if ( - sample["uid"] != self.mountedPin_pv.get() - and getBlConfig("queueCollect") == 0 - ): # Don't fill data paths if an unmounted sample is clicked and queue collect is off - return - if self.osc_start_ledit.text() == "": - self.selectedSampleRequest = daq_utils.createDefaultRequest( - itemData, createVisit=False - ) - self.refreshCollectionParams(self.selectedSampleRequest) - if self.stillModeStatePV.get(): - self.setGuiValues({"osc_range": "0.0"}) - reqObj = self.selectedSampleRequest["request_obj"] - self.dataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) - self.dataPathGB.setBasePath_ledit(reqObj["basePath"]) - self.dataPathGB.setDataPath_ledit(reqObj["directory"]) - self.EScanDataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) - self.EScanDataPathGB.setBasePath_ledit(reqObj["basePath"]) - self.EScanDataPathGB.setDataPath_ledit(reqObj["directory"]) - self.EScanDataPathGB.setFileNumstart_ledit( - str(reqObj["file_number_start"]) - ) - if self.vidActionRasterDefRadio.isChecked(): - self.protoComboBox.setCurrentIndex( - self.protoComboBox.findText(str("raster")) - ) - self.showProtParams() - else: - self.selectedSampleRequest = daq_utils.createDefaultRequest( - itemData, createVisit=False - ) - reqObj = self.selectedSampleRequest["request_obj"] - self.dataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) - self.dataPathGB.setBasePath_ledit(reqObj["basePath"]) - self.dataPathGB.setDataPath_ledit(reqObj["directory"]) - self.EScanDataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) - self.EScanDataPathGB.setBasePath_ledit(reqObj["basePath"]) - self.EScanDataPathGB.setDataPath_ledit(reqObj["directory"]) - self.EScanDataPathGB.setFileNumstart_ledit( - str(reqObj["file_number_start"]) - ) - else: # request - self.selectedSampleRequest = db_lib.getRequestByID(itemData) - reqObj = self.selectedSampleRequest["request_obj"] - reqID = self.selectedSampleRequest["uid"] - self.selectedSampleID = self.selectedSampleRequest["sample"] - sample = db_lib.getSampleByID(self.selectedSampleID) - if ( - sample["uid"] != self.mountedPin_pv.get() - and getBlConfig("queueCollect") == 0 - ): # Don't fill request data if unmounted sample and queuecollect is off - return - owner = sample["owner"] - if reqObj["protocol"] == "eScan": - try: - if reqObj["runChooch"]: - resultList = db_lib.getResultsforRequest(reqID) - if len(resultList) > 0: - lastResult = resultList[-1] - if ( - db_lib.getResult(lastResult["uid"])["result_type"] - == "choochResult" - ): - resultID = lastResult["uid"] - logger.info("plotting chooch") - self.processChoochResult(resultID) - except KeyError: - logger.error( - "KeyError - ignoring chooch-related items, perhaps from a bad energy scan" - ) - self.refreshCollectionParams(self.selectedSampleRequest) - - def processXrecRasterCB(self, value=None, char_value=None, **kw): - xrecFlag = value - if xrecFlag != "0": - self.xrecRasterSignal.emit(xrecFlag) - - def processChoochResultsCB(self, value=None, char_value=None, **kw): - choochFlag = value - if choochFlag != "0": - self.choochResultSignal.emit(choochFlag) - - def processEnergyChangeCB(self, value=None, char_value=None, **kw): - energyVal = value - self.energyChangeSignal.emit(energyVal) - - def mountedPinChangedCB(self, value=None, char_value=None, **kw): - mountedPinPos = value - self.mountedPinSignal.emit(mountedPinPos) - - def beamSizeChangedCB(self, value=None, char_value=None, **kw): - beamSizeFlag = value - self.beamSizeSignal.emit(beamSizeFlag) - - def controlMasterChangedCB(self, value=None, char_value=None, **kw): - controlMasterPID = value - self.controlMasterSignal.emit(controlMasterPID) - - def zebraArmStateChangedCB(self, value=None, char_value=None, **kw): - armState = value - self.zebraArmStateSignal.emit(armState) - - def govRobotSeReachChangedCB(self, value=None, char_value=None, **kw): - armState = value - self.govRobotSeReachSignal.emit(armState) - - def govRobotSaReachChangedCB(self, value=None, char_value=None, **kw): - armState = value - self.govRobotSaReachSignal.emit(armState) - - def govRobotDaReachChangedCB(self, value=None, char_value=None, **kw): - armState = value - self.govRobotDaReachSignal.emit(armState) - - def govRobotBlReachChangedCB(self, value=None, char_value=None, **kw): - armState = value - self.govRobotBlReachSignal.emit(armState) - - def detMessageChangedCB(self, value=None, char_value=None, **kw): - state = char_value - self.detMessageSignal.emit(state) - - def sampleFluxChangedCB(self, value=None, char_value=None, **kw): - state = value - self.sampleFluxSignal.emit(state) - - def zebraPulseStateChangedCB(self, value=None, char_value=None, **kw): - state = value - self.zebraPulseStateSignal.emit(state) - - def stillModeStateChangedCB(self, value=None, char_value=None, **kw): - state = value - self.stillModeStateSignal.emit(state) - - def zebraDownloadStateChangedCB(self, value=None, char_value=None, **kw): - state = value - self.zebraDownloadStateSignal.emit(state) - - def zebraSentTriggerStateChangedCB(self, value=None, char_value=None, **kw): - state = value - self.zebraSentTriggerStateSignal.emit(state) - - def zebraReturnedTriggerStateChangedCB(self, value=None, char_value=None, **kw): - state = value - self.zebraReturnedTriggerStateSignal.emit(state) - - def shutterChangedCB(self, value=None, char_value=None, **kw): - shutterVal = value - self.fastShutterSignal.emit(shutterVal) - - def gripTempChangedCB(self, value=None, char_value=None, **kw): - gripVal = value - self.gripTempSignal.emit(gripVal) - - def cryostreamTempChangedCB(self, value=None, char_value=None, **kw): - cryostreamTemp = value - self.cryostreamTempSignal.emit(cryostreamTemp) - - def ringCurrentChangedCB(self, value=None, char_value=None, **kw): - ringCurrentVal = value - self.ringCurrentSignal.emit(ringCurrentVal) - - def beamAvailableChangedCB(self, value=None, char_value=None, **kw): - beamAvailableVal = value - self.beamAvailableSignal.emit(beamAvailableVal) - - def sampleExposedChangedCB(self, value=None, char_value=None, **kw): - sampleExposedVal = value - self.sampleExposedSignal.emit(sampleExposedVal) - - def processSampMoveCB(self, value=None, char_value=None, **kw): - posRBV = value - motID = kw["motID"] - self.sampMoveSignal.emit(posRBV, motID) - - def processROIChangeCB(self, value=None, char_value=None, **kw): - posRBV = value - ID = kw["ID"] - self.roiChangeSignal.emit(posRBV, ID) - - def processHighMagCursorChangeCB(self, value=None, char_value=None, **kw): - posRBV = value - ID = kw["ID"] - self.highMagCursorChangeSignal.emit(posRBV, ID) - - def processLowMagCursorChangeCB(self, value=None, char_value=None, **kw): - posRBV = value - ID = kw["ID"] - self.lowMagCursorChangeSignal.emit(posRBV, ID) - - def treeChangedCB(self, value=None, char_value=None, **kw): - if self.processID != self.treeChanged_pv.get(): - self.refreshTreeSignal.emit() - - def serverMessageCB(self, value=None, char_value=None, **kw): - serverMessageVar = char_value - self.serverMessageSignal.emit(serverMessageVar) - - def serverPopupMessageCB(self, value=None, char_value=None, **kw): - serverMessageVar = char_value - self.serverPopupMessageSignal.emit(serverMessageVar) - - def programStateCB(self, value=None, char_value=None, **kw): - programStateVar = value - self.programStateSignal.emit(programStateVar) - - def pauseButtonStateCB(self, value=None, char_value=None, **kw): - pauseButtonStateVar = value - self.pauseButtonStateSignal.emit(pauseButtonStateVar) - - def initOphyd(self): - if daq_utils.beamline == "nyx": - # initialize devices - self.gon = GonioDevice("XF:19IDC-ES{MD2}:", name="gonio") - self.camera = CameraDevice("XF:19IDC-ES{MD2}:", name="camera") - self.md2 = MD2Device("XF:19IDC-ES{MD2}:", name="md2") - self.front_light = LightDevice("XF:19IDC-ES{MD2}:Front", name="front_light") - self.back_light = LightDevice("XF:19IDC-ES{MD2}:Back", name="back_light") - self.aperture = MD2ApertureDevice("XF:19IDC-ES{MD2}:", name="aperture") - else: - pass - - def initUI(self): - self.tabs = QtWidgets.QTabWidget() - self.comm_pv = PV(daq_utils.beamlineComm + "command_s") - self.immediate_comm_pv = PV(daq_utils.beamlineComm + "immediate_command_s") - self.stillModeStatePV = PV(daq_utils.pvLookupDict["stillModeStatus"]) - self.progressDialog = QtWidgets.QProgressDialog() - self.progressDialog.setCancelButtonText("Cancel") - self.progressDialog.setModal(False) - self.progressDialog.reset() - tab1 = QtWidgets.QWidget() - vBoxlayout1 = QtWidgets.QVBoxLayout() - splitter1 = QtWidgets.QSplitter(QtCore.Qt.Vertical, self) - splitter1.addWidget(self.tabs) - self.setCentralWidget(splitter1) - splitterSizes = [600, 100] - importAction = QtWidgets.QAction("Import Spreadsheet...", self) - importAction.triggered.connect(self.popImportDialogCB) - modeGroup = QtWidgets.QActionGroup(self) - modeGroup.setExclusive(True) - self.userAction = QtWidgets.QAction("User Mode", self, checkable=True) - self.userAction.triggered.connect(self.setUserModeCB) - self.userAction.setChecked(True) - self.expertAction = QtWidgets.QAction("Expert Mode", self, checkable=True) - self.expertAction.triggered.connect(self.setExpertModeCB) - self.staffAction = QtWidgets.QAction("Staff Panel...", self) - self.staffAction.triggered.connect(self.popStaffDialogCB) - modeGroup.addAction(self.userAction) - modeGroup.addAction(self.expertAction) - exitAction = QtWidgets.QAction(QtGui.QIcon("exit24.png"), "Exit", self) - exitAction.setShortcut("Ctrl+Q") - exitAction.setStatusTip("Exit application") - exitAction.triggered.connect(self.closeAll) - self.statusBar() - self.queue_collect_status_widget = QtWidgets.QLabel("Queue Collect: ON") - self.statusBar().addPermanentWidget(self.queue_collect_status_widget) - menubar = self.menuBar() - fileMenu = menubar.addMenu("&File") - settingsMenu = menubar.addMenu("Settings") - fileMenu.addAction(importAction) - fileMenu.addAction(self.userAction) - fileMenu.addAction(self.expertAction) - fileMenu.addAction(self.staffAction) - # Define all of the available actions for the overlay color group - self.BlueOverlayAction = QtWidgets.QAction("Blue", self, checkable=True) - self.RedOverlayAction = QtWidgets.QAction("Red", self, checkable=True) - self.GreenOverlayAction = QtWidgets.QAction("Green", self, checkable=True) - self.WhiteOverlayAction = QtWidgets.QAction("White", self, checkable=True) - self.BlackOverlayAction = QtWidgets.QAction("Black", self, checkable=True) - # Connect all of the trigger callbacks to their respective actions - self.BlueOverlayAction.triggered.connect(self.blueOverlayTriggeredCB) - self.RedOverlayAction.triggered.connect(self.redOverlayTriggeredCB) - self.GreenOverlayAction.triggered.connect(self.greenOverlayTriggeredCB) - self.WhiteOverlayAction.triggered.connect(self.whiteOverlayTriggeredCB) - self.BlackOverlayAction.triggered.connect(self.blackOverlayTriggeredCB) - # Create the action group and populate it - self.overlayColorActionGroup = QtWidgets.QActionGroup(self) - self.overlayColorActionGroup.setExclusive(True) - self.overlayColorActionGroup.addAction(self.BlueOverlayAction) - self.overlayColorActionGroup.addAction(self.RedOverlayAction) - self.overlayColorActionGroup.addAction(self.GreenOverlayAction) - self.overlayColorActionGroup.addAction(self.WhiteOverlayAction) - self.overlayColorActionGroup.addAction(self.BlackOverlayAction) - # Create the menu item with the submenu, add the group - self.overlayMenu = settingsMenu.addMenu("Overlay Settings") - self.overlayMenu.addActions(self.overlayColorActionGroup.actions()) - try: - if getBlConfig("defaultOverlayColor") == "GREEN": - self.GreenOverlayAction.setChecked(True) - else: - self.BlueOverlayAction.setChecked(True) - except KeyError as e: - logger.warning("No value for defaultOverlayColor") - self.BlueOverlayAction.setChecked(True) - - fileMenu.addAction(exitAction) - self.setGeometry(300, 300, 1550, 1000) # width and height here. - self.setWindowTitle("LSDC on %s" % daq_utils.beamline) - self.show() - - def blueOverlayTriggeredCB(self): - overlayBrush = QtGui.QBrush(QtCore.Qt.blue) - self.centerMarker.setBrush(overlayBrush) - self.imageScale.setPen(QtGui.QPen(overlayBrush, 2.0)) - self.imageScaleText.setPen(QtGui.QPen(overlayBrush, 1.0)) - - def redOverlayTriggeredCB(self): - overlayBrush = QtGui.QBrush(QtCore.Qt.red) - self.centerMarker.setBrush(overlayBrush) - self.imageScale.setPen(QtGui.QPen(overlayBrush, 2.0)) - self.imageScaleText.setPen(QtGui.QPen(overlayBrush, 1.0)) - - def greenOverlayTriggeredCB(self): - overlayBrush = QtGui.QBrush(QtCore.Qt.green) - self.centerMarker.setBrush(overlayBrush) - self.imageScale.setPen(QtGui.QPen(overlayBrush, 2.0)) - self.imageScaleText.setPen(QtGui.QPen(overlayBrush, 1.0)) - - def whiteOverlayTriggeredCB(self): - overlayBrush = QtGui.QBrush(QtCore.Qt.white) - self.centerMarker.setBrush(overlayBrush) - self.imageScale.setPen(QtGui.QPen(overlayBrush, 2.0)) - self.imageScaleText.setPen(QtGui.QPen(overlayBrush, 1.0)) - - def blackOverlayTriggeredCB(self): - overlayBrush = QtGui.QBrush(QtCore.Qt.black) - self.centerMarker.setBrush(overlayBrush) - self.imageScale.setPen(QtGui.QPen(overlayBrush, 2.0)) - self.imageScaleText.setPen(QtGui.QPen(overlayBrush, 1.0)) - - def popStaffDialogCB(self): - if self.controlEnabled(): - self.staffScreenDialog.show() - else: - self.popupServerMessage("You don't have control") - - def closeAll(self): - self.hutchCornerCamThread.stop() - self.hutchTopCamThread.stop() - self.hutchCornerCamThread.wait() - self.hutchTopCamThread.wait() - QtWidgets.QApplication.instance().quit() - - def initCallbacks(self): - self.beamSizeSignal.connect(self.processBeamSize) - self.beamSize_pv.add_callback(self.beamSizeChangedCB) - - self.treeChanged_pv = PV(daq_utils.beamlineComm + "live_q_change_flag") - self.refreshTreeSignal.connect(self.dewarTree.refreshTree) - self.treeChanged_pv.add_callback(self.treeChangedCB) - self.mountedPin_pv = PV(daq_utils.beamlineComm + "mounted_pin") - self.mountedPinSignal.connect(self.processMountedPin) - self.mountedPin_pv.add_callback(self.mountedPinChangedCB) - det_stop_pv = daq_utils.pvLookupDict["stopEiger"] - logger.info("setting stop Eiger detector PV: %s" % det_stop_pv) - self.stopDet_pv = PV(det_stop_pv) - det_reboot_pv = daq_utils.pvLookupDict["eigerIOC_reboot"] - logger.info("setting detector ioc reboot PV: %s" % det_reboot_pv) - self.rebootDetIOC_pv = PV(det_reboot_pv) - rz_pv = daq_utils.pvLookupDict["zebraReset"] - logger.info("setting zebra reset PV: %s" % rz_pv) - self.resetZebra_pv = PV(rz_pv) - rz_reboot_pv = daq_utils.pvLookupDict["zebraRebootIOC"] - logger.info("setting zebra reboot ioc PV: %s" % rz_reboot_pv) - self.rebootZebraIOC_pv = PV(rz_reboot_pv) - self.zebraArmedPV = PV(daq_utils.pvLookupDict["zebraArmStatus"]) - self.zebraArmStateSignal.connect(self.processZebraArmState) - self.zebraArmedPV.add_callback(self.zebraArmStateChangedCB) - - self.govRobotSeReachPV = PV(daq_utils.pvLookupDict["govRobotSeReach"]) - self.govRobotSeReachSignal.connect(self.processGovRobotSeReach) - self.govRobotSeReachPV.add_callback(self.govRobotSeReachChangedCB) - - self.govRobotSaReachPV = PV(daq_utils.pvLookupDict["govRobotSaReach"]) - self.govRobotSaReachSignal.connect(self.processGovRobotSaReach) - self.govRobotSaReachPV.add_callback(self.govRobotSaReachChangedCB) - - self.govRobotDaReachPV = PV(daq_utils.pvLookupDict["govRobotDaReach"]) - self.govRobotDaReachSignal.connect(self.processGovRobotDaReach) - self.govRobotDaReachPV.add_callback(self.govRobotDaReachChangedCB) - - self.govRobotBlReachPV = PV(daq_utils.pvLookupDict["govRobotBlReach"]) - self.govRobotBlReachSignal.connect(self.processGovRobotBlReach) - self.govRobotBlReachPV.add_callback(self.govRobotBlReachChangedCB) - - self.detectorMessagePV = PV(daq_utils.pvLookupDict["eigerStatMessage"]) - self.detMessageSignal.connect(self.processDetMessage) - self.detectorMessagePV.add_callback(self.detMessageChangedCB) - - self.sampleFluxSignal.connect(self.processSampleFlux) - self.sampleFluxPV.add_callback(self.sampleFluxChangedCB) - - self.stillModeStateSignal.connect(self.processStillModeState) - self.stillModeStatePV.add_callback(self.stillModeStateChangedCB) - - self.zebraPulsePV = PV(daq_utils.pvLookupDict["zebraPulseStatus"]) - self.zebraPulseStateSignal.connect(self.processZebraPulseState) - self.zebraPulsePV.add_callback(self.zebraPulseStateChangedCB) - - self.zebraDownloadPV = PV(daq_utils.pvLookupDict["zebraDownloading"]) - self.zebraDownloadStateSignal.connect(self.processZebraDownloadState) - self.zebraDownloadPV.add_callback(self.zebraDownloadStateChangedCB) - - self.zebraSentTriggerPV = PV(daq_utils.pvLookupDict["zebraSentTriggerStatus"]) - self.zebraSentTriggerStateSignal.connect(self.processZebraSentTriggerState) - self.zebraSentTriggerPV.add_callback(self.zebraSentTriggerStateChangedCB) - - self.zebraReturnedTriggerPV = PV( - daq_utils.pvLookupDict["zebraTriggerReturnStatus"] - ) - self.zebraReturnedTriggerStateSignal.connect( - self.processZebraReturnedTriggerState - ) - self.zebraReturnedTriggerPV.add_callback( - self.zebraReturnedTriggerStateChangedCB - ) - - self.controlMaster_pv = PV(daq_utils.beamlineComm + "zinger_flag") - self.controlMasterSignal.connect(self.processControlMaster) - self.controlMaster_pv.add_callback(self.controlMasterChangedCB) - - self.beamCenterX_pv = PV(daq_utils.pvLookupDict["beamCenterX"]) - self.beamCenterY_pv = PV(daq_utils.pvLookupDict["beamCenterY"]) - - self.choochResultFlag_pv = PV(daq_utils.beamlineComm + "choochResultFlag") - self.choochResultSignal.connect(self.processChoochResult) - self.choochResultFlag_pv.add_callback(self.processChoochResultsCB) - self.xrecRasterFlag_pv = PV(daq_utils.beamlineComm + "xrecRasterFlag") - self.xrecRasterFlag_pv.put("0") - self.xrecRasterSignal.connect(self.displayXrecRaster) - self.xrecRasterFlag_pv.add_callback(self.processXrecRasterCB) - self.message_string_pv = PV(daq_utils.beamlineComm + "message_string") - self.serverMessageSignal.connect(self.printServerMessage) - self.message_string_pv.add_callback(self.serverMessageCB) - self.popup_message_string_pv = PV( - daq_utils.beamlineComm + "gui_popup_message_string" - ) - self.serverPopupMessageSignal.connect(self.popupServerMessage) - self.popup_message_string_pv.add_callback(self.serverPopupMessageCB) - self.program_state_pv = PV(daq_utils.beamlineComm + "program_state") - self.programStateSignal.connect(self.colorProgramState) - self.program_state_pv.add_callback(self.programStateCB) - self.pause_button_state_pv = PV(daq_utils.beamlineComm + "pause_button_state") - self.pauseButtonStateSignal.connect(self.changePauseButtonState) - self.pause_button_state_pv.add_callback(self.pauseButtonStateCB) - - self.energyChangeSignal.connect(self.processEnergyChange) - self.energy_pv.add_callback(self.processEnergyChangeCB, motID="x") - - self.sampx_pv = PV(self.gon.x.readback.pvname) - self.sampMoveSignal.connect(self.processSampMove) - self.sampx_pv.add_callback(self.processSampMoveCB, motID="x") - self.sampy_pv = PV(self.gon.y.readback.pvname) - self.sampy_pv.add_callback(self.processSampMoveCB, motID="y") - self.sampz_pv = PV(self.gon.z.readback.pvname) - self.sampz_pv.add_callback(self.processSampMoveCB, motID="z") - - if self.scannerType == "PI": - self.sampFineX_pv = PV(daq_utils.motor_dict["fineX"] + ".RBV") - self.sampFineX_pv.add_callback(self.processSampMoveCB, motID="fineX") - self.sampFineY_pv = PV(daq_utils.motor_dict["fineY"] + ".RBV") - self.sampFineY_pv.add_callback(self.processSampMoveCB, motID="fineY") - self.sampFineZ_pv = PV(daq_utils.motor_dict["fineZ"] + ".RBV") - self.sampFineZ_pv.add_callback(self.processSampMoveCB, motID="fineZ") - - self.omega_pv = PV(self.gon.omega.setpoint.pvname) - self.omegaTweak_pv = PV(self.gon.omega.setpoint.pvname) - self.sampyTweak_pv = PV(self.gon.y.setpoint.pvname) - #if daq_utils.beamline == "nyx": - # self.sampzTweak_pv = PV(self.gon.x.setpoint.pvname + ".RLV") - #else: - self.sampzTweak_pv = PV(self.gon.z.setpoint.pvname) - self.omegaRBV_pv = PV(self.gon.omega.readback.pvname) - self.omegaRBV_pv.add_callback( - self.processSampMoveCB, motID="omega" - ) # I think monitoring this allows for the textfield to monitor val and this to deal with the graphics. Else next line has two callbacks on same thing. - self.photonShutterOpen_pv = PV(daq_utils.pvLookupDict["photonShutterOpen"]) - self.photonShutterClose_pv = PV(daq_utils.pvLookupDict["photonShutterClose"]) - self.fastShutterRBV_pv = PV(daq_utils.motor_dict["fastShutter"] + ".RBV") - self.fastShutterSignal.connect(self.processFastShutter) - self.fastShutterRBV_pv.add_callback(self.shutterChangedCB) - self.gripTempSignal.connect(self.processGripTemp) - self.gripTemp_pv.add_callback(self.gripTempChangedCB) - if getBlConfig(CRYOSTREAM_ONLINE): - self.cryostreamTempSignal.connect(self.processCryostreamTemp) - self.cryostreamTemp_pv.add_callback(self.cryostreamTempChangedCB) - self.ringCurrentSignal.connect(self.processRingCurrent) - self.ringCurrent_pv.add_callback(self.ringCurrentChangedCB) - self.beamAvailableSignal.connect(self.processBeamAvailable) - self.beamAvailable_pv.add_callback(self.beamAvailableChangedCB) - self.sampleExposedSignal.connect(self.processSampleExposed) - self.sampleExposed_pv.add_callback(self.sampleExposedChangedCB) - self.highMagCursorChangeSignal.connect(self.processHighMagCursorChange) - self.highMagCursorX_pv.add_callback(self.processHighMagCursorChangeCB, ID="x") - self.highMagCursorY_pv.add_callback(self.processHighMagCursorChangeCB, ID="y") - self.lowMagCursorChangeSignal.connect(self.processLowMagCursorChange) - self.lowMagCursorX_pv.add_callback(self.processLowMagCursorChangeCB, ID="x") - self.lowMagCursorY_pv.add_callback(self.processLowMagCursorChangeCB, ID="y") - - def popupServerMessage(self, message_s): - if self.popUpMessageInit: - self.popUpMessageInit = 0 - return - self.popupMessage.done(1) - if message_s == "killMessage": - return - else: - self.popupMessage.showMessage(message_s) - - def printServerMessage(self, message_s): - if self.textWindowMessageInit: - self.textWindowMessageInit = 0 - return - logger.info(message_s) - print(message_s) - - def colorProgramState(self, programState_s): - if programState_s.find("Ready") == -1: - self.statusLabel.setColor("yellow") - else: - self.statusLabel.setColor("#99FF66") - - def changePauseButtonState(self, buttonState_s): - self.pauseQueueButton.setText(buttonState_s) - if buttonState_s.find("Pause") != -1: - self.pauseQueueButton.setStyleSheet("background-color: None") - else: - self.pauseQueueButton.setStyleSheet("background-color: yellow") - - def controlEnabled(self): - return ( - self.processID == abs(int(self.controlMaster_pv.get())) - and self.controlMasterCheckBox.isChecked() - ) - - def send_to_server(self, s): - if s == "lockControl": - self.controlMaster_pv.put(0 - self.processID) - return - if s == "unlockControl": - self.controlMaster_pv.put(self.processID) - return - if self.controlEnabled(): - time.sleep(0.01) - logger.info("send_to_server: %s" % s) - self.comm_pv.put(s) - else: - self.popupServerMessage("You don't have control") - - def aux_send_to_server(self, s): - if self.controlEnabled(): - time.sleep(0.01) - logger.info("aux_send_to_server: %s" % s) - self.immediate_comm_pv.put(s) - else: - self.popupServerMessage("You don't have control") diff --git a/gui/data_loc_info.py b/gui/data_loc_info.py deleted file mode 100644 index 999c1839..00000000 --- a/gui/data_loc_info.py +++ /dev/null @@ -1,113 +0,0 @@ -import logging -import os -import typing - -from qtpy import QtCore, QtGui, QtWidgets - -import daq_utils -import db_lib -from config_params import VALID_PREFIX_LENGTH, VALID_PREFIX_NAME - -if typing.TYPE_CHECKING: - from lsdcGui import ControlMain - -logger = logging.getLogger() - - -class DataLocInfo(QtWidgets.QGroupBox): - def __init__(self, parent: "ControlMain"): - QtWidgets.QGroupBox.__init__(self, parent) - self.parent = parent - self.setTitle("Data Location") - self.vBoxDPathParams1 = QtWidgets.QVBoxLayout() - self.hBoxDPathParams1 = QtWidgets.QHBoxLayout() - self.basePathLabel = QtWidgets.QLabel("Base Path:") - self.base_path_ledit = QtWidgets.QLabel() - self.base_path_ledit.setText(daq_utils.getBlConfig("visitDirectory")) - self.base_path_ledit.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) - # self.base_path_ledit.textChanged[str].connect(self.basePathTextChanged) - self.browseBasePathButton = QtWidgets.QPushButton("Browse...") - self.browseBasePathButton.setEnabled(False) - # self.browseBasePathButton.clicked.connect(self.parent.popBaseDirectoryDialogCB) - self.hBoxDPathParams1.addWidget(self.basePathLabel) - self.hBoxDPathParams1.addWidget(self.base_path_ledit) - self.hBoxDPathParams1.addWidget(self.browseBasePathButton) - self.hBoxDPathParams2 = QtWidgets.QHBoxLayout() - self.dataPrefixLabel = QtWidgets.QLabel( - "Data Prefix:\n(%s Char Limit)" % VALID_PREFIX_LENGTH - ) - self.prefix_ledit = QtWidgets.QLineEdit() - self.prefix_ledit.textChanged[str].connect(self.prefixTextChanged) - self.prefix_ledit.setValidator( - QtGui.QRegExpValidator(QtCore.QRegExp(VALID_PREFIX_NAME), self.prefix_ledit) - ) - self.hBoxDPathParams2.addWidget(self.dataPrefixLabel) - self.hBoxDPathParams2.addWidget(self.prefix_ledit) - self.dataNumstartLabel = QtWidgets.QLabel("File Number Start:") - self.file_numstart_ledit = QtWidgets.QLineEdit() - self.file_numstart_ledit.setValidator(QtGui.QIntValidator(1, 99999, self)) - self.file_numstart_ledit.setFixedWidth(50) - self.hBoxDPathParams3 = QtWidgets.QHBoxLayout() - self.dataPathLabel = QtWidgets.QLabel("Data Path:") - self.dataPath_ledit = QtWidgets.QLineEdit() - self.dataPath_ledit.setFrame(False) - self.dataPath_ledit.setReadOnly(True) - self.hBoxDPathParams3.addWidget(self.dataPathLabel) - self.hBoxDPathParams3.addWidget(self.dataPath_ledit) - self.hBoxDPathParams2.addWidget(self.dataNumstartLabel) - self.hBoxDPathParams2.addWidget(self.file_numstart_ledit) - self.vBoxDPathParams1.addLayout(self.hBoxDPathParams1) - self.vBoxDPathParams1.addLayout(self.hBoxDPathParams2) - self.vBoxDPathParams1.addLayout(self.hBoxDPathParams3) - self.setLayout(self.vBoxDPathParams1) - - def basePathTextChanged(self, text): - prefix = self.prefix_ledit.text() - self.setDataPath_ledit( - text + "/" + str(daq_utils.getVisitName()) + "/" + prefix + "/#/" - ) - - def prefixTextChanged(self, text): - prefix = self.prefix_ledit.text() - try: - runNum = db_lib.getSampleRequestCount(self.parent.selectedSampleID) - except KeyError: - logger.error("just setting a value of 1 for now") - runNum = 1 - try: - ( - puckPosition, - samplePositionInContainer, - containerID, - ) = db_lib.getCoordsfromSampleID( - daq_utils.beamline, self.parent.selectedSampleID - ) - except IndexError: - logger.error("IndexError returning") - return - self.setDataPath_ledit( - self.base_path_ledit.text() - + "/" - + str(daq_utils.getVisitName()) - + "/" - + prefix - + "/" - + str(runNum + 1) - + "/" - + db_lib.getContainerNameByID(containerID) - + "_" - + str(samplePositionInContainer + 1) - + "/" - ) - - def setFileNumstart_ledit(self, s): - self.file_numstart_ledit.setText(s) - - def setFilePrefix_ledit(self, s): - self.prefix_ledit.setText(s) - - def setBasePath_ledit(self, s): - self.base_path_ledit.setText(s) - - def setDataPath_ledit(self, s): - self.dataPath_ledit.setText(s) diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py deleted file mode 100644 index 355dbc53..00000000 --- a/gui/dewar_tree.py +++ /dev/null @@ -1,493 +0,0 @@ -import logging -import typing -import os, getpass -from qtpy import QtCore, QtGui, QtWidgets -from qtpy.QtCore import Qt -import requests -import daq_utils -import db_lib -from config_params import ( - DEWAR_SECTORS, - PUCKS_PER_DEWAR_SECTOR, - SAMPLE_TIMER_DELAY, - IS_STAFF, -) - -if typing.TYPE_CHECKING: - from lsdcGui import ControlMain - -logger = logging.getLogger() - -global sampleDict -sampleDict = {} - -global containerDict -containerDict = {} -ICON = ":/trolltech/styles/commonstyle/images/file-16.png" - - -class DewarTree(QtWidgets.QTreeView): - def __init__(self, parent: "ControlMain"): - super(DewarTree, self).__init__(parent) - self.pucksPerDewarSector = PUCKS_PER_DEWAR_SECTOR[daq_utils.beamline] - self.dewarSectors = DEWAR_SECTORS[daq_utils.beamline] - self.parent = parent - self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) - self.setAnimated(True) - self.model = QtGui.QStandardItemModel() - - # self.isExpanded = 1 - self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - self.customContextMenuRequested.connect(self.openMenu) - # Keeps track of whether the user is part of a proposal - self.proposal_membership = {} - - def openMenu(self, position): - indexes = self.selectedIndexes() - selectedLevels = set() - if indexes: - for index in indexes: - level = 0 - while index.parent().isValid(): - index = index.parent() - level += 1 - selectedLevels.add(level) - if len(selectedLevels) == 1: - level = list(selectedLevels)[0] - menu = QtWidgets.QMenu() - if level == 2: # This is usually a request - deleteReqAction = QtWidgets.QAction( - "Delete selected request(s)", self - ) - deleteReqAction.triggered.connect(self.deleteSelectedCB) - cloneReqAction = QtWidgets.QAction("Clone selected request", self) - cloneReqAction.triggered.connect(self.cloneRequestCB) - queueSelAction = QtWidgets.QAction( - "Queue selected request(s)", self - ) - queueSelAction.triggered.connect(self.queueAllSelectedCB) - dequeueSelAction = QtWidgets.QAction( - "Dequeue selected request(s)", self - ) - dequeueSelAction.triggered.connect(self.deQueueAllSelectedCB) - menu.addAction(cloneReqAction) - menu.addAction(queueSelAction) - menu.addAction(dequeueSelAction) - menu.addSeparator() - menu.addAction(deleteReqAction) - menu.exec_(self.viewport().mapToGlobal(position)) - - def cloneRequestCB(self): - # Only the first selected request is cloned (If multiple are chosen) - index = self.selectedIndexes()[0] - item = self.model.itemFromIndex(index) - requestData = db_lib.getRequestByID(item.data(32)) - protocol = requestData["request_obj"]["protocol"] - if "raster" in protocol.lower(): # Will cover specRaster and stepRaster as well - self.parent.cloneRequestCB() - elif "standard" in protocol.lower(): - self.parent.addRequestsToAllSelectedCB() - - def keyPressEvent(self, event): - if event.key() == Qt.Key_Delete or event.key() == Qt.Key_Backspace: - self.deleteSelectedCB(0) - else: - super(DewarTree, self).keyPressEvent(event) - - def refreshTree(self): - self.parent.dewarViewToggleCheckCB() - - def set_mounted_sample(self, item): - # Formats the text of the item that is passed in as the mounted sample - item.setForeground(QtGui.QColor("red")) - font = QtGui.QFont() - font.setUnderline(True) - font.setItalic(True) - font.setOverline(True) - item.setFont(font) - - def refreshTreeDewarView(self): - puck = "" - self.model.clear() - dewarContents = db_lib.getContainerByName( - daq_utils.primaryDewarName, daq_utils.beamline - )["content"] - parentItem = self.model.invisibleRootItem() - for i, puck_id in enumerate( - dewarContents - ): # dewar contents is the list of puck IDs - puck = "" - puckName = "" - if puck_id: - puck = containerDict.get( - puck_id, - containerDict.setdefault(puck_id, db_lib.getContainerByID(puck_id)), - ) - puckName = puck["name"] - sector, puck_pos = divmod(i, self.pucksPerDewarSector) - index_s = f"{sector+1}{chr(puck_pos + ord('A'))}" - item = QtGui.QStandardItem(QtGui.QIcon(ICON), f"{index_s} {puckName}") - item.setData(puckName, 32) - item.setData("container", 33) - parentItem.appendRow(item) - if puck != "" and puckName != "private": - puckContents = puck.get("content", []) - self.add_samples_to_puck_tree(puckContents, item, index_s) - self.setModel(self.model) - self.model.itemChanged.connect(self.queueSelectedSample) - if self.isExpanded: - self.expandAll() - else: - self.collapseAll() - self.scrollTo(self.currentIndex(), QtWidgets.QAbstractItemView.PositionAtCenter) - - def add_samples_to_puck_tree( - self, puckContents, parentItem: QtGui.QStandardItem, index_label - ): - # Method will attempt to add samples to the puck. If you don't belong to the proposal, - # it will not add samples and clear the puck information - selectedIndex = None - mountedIndex = None - selectedSampleIndex = None - collectionRunning = False - for j, sample_id in enumerate(puckContents): - if not sample_id: - # this is an empty spot, no sample - position_s = str(j + 1) - item = QtGui.QStandardItem(QtGui.QIcon(ICON), position_s) - item.setData("", 32) - parentItem.appendRow(item) - continue - - sample = sampleDict.get( - sample_id, - sampleDict.setdefault(sample_id, db_lib.getSampleByID(sample_id)), - ) - - if not IS_STAFF and not self.is_proposal_member(sample["proposalID"]): - # If the user is not part of the proposal and is not staff, don't fill tree - # Clear the puck information and don't make it selectable - parentItem.setText(index_label) - current_flags = parentItem.flags() - parentItem.setFlags(current_flags & ~Qt.ItemFlag.ItemIsSelectable) # type: ignore - position_s = f'{j+1}-{sample.get("name", "")}' - item = QtGui.QStandardItem( - QtGui.QIcon(ICON), - position_s, - ) - return - - parentItem.setText(f"{index_label} pass-{sample['proposalID']}") - - position_s = f'{j+1}-{sample.get("name", "")}' - item = QtGui.QStandardItem( - QtGui.QIcon(ICON), - position_s, - ) - # just stuck sampleID there, but negate it to diff from reqID - item.setData(sample_id, 32) - item.setData("sample", 33) - if sample_id == self.parent.mountedPin_pv.get(): - self.set_mounted_sample(item) - parentItem.appendRow(item) - if sample_id == self.parent.mountedPin_pv.get(): - mountedIndex = self.model.indexFromItem(item) - # looking for the selected item - if sample_id == self.parent.selectedSampleID: - logger.info("found " + str(self.parent.SelectedItemData)) - selectedSampleIndex = self.model.indexFromItem(item) - sampleRequestList = db_lib.getRequestsBySampleID(sample_id) - for request in sampleRequestList: - if not ("protocol" in request["request_obj"]): - continue - col_item = self.create_request_item(request) - if request["priority"] == 99999: - selectedIndex = self.model.indexFromItem( - col_item - ) ##attempt to leave it on the request after collection - collectionRunning = True - item.appendRow(col_item) - if ( - request["uid"] == self.parent.SelectedItemData - ): # looking for the selected item, this is a request - selectedIndex = self.model.indexFromItem(col_item) - - current_index = None - if not collectionRunning: - if selectedSampleIndex: - current_index = selectedSampleIndex - elif mountedIndex: - current_index = mountedIndex - item = self.model.itemFromIndex(mountedIndex) - self.set_mounted_sample(item) - elif selectedIndex: - current_index = selectedIndex - elif collectionRunning and mountedIndex: - current_index = mountedIndex - - if current_index: - self.setCurrentIndex(current_index) - self.parent.row_clicked(current_index) - - def is_proposal_member(self, proposal_id) -> bool: - # Check if the user running LSDC is part of the sample's proposal - if proposal_id not in self.proposal_membership: - r = requests.get(f"{os.environ['NSLS2_API_URL']}/proposal/{proposal_id}") - r.raise_for_status() - response = r.json() - if "users" in response and getpass.getuser() in [ - user["username"] for user in response["users"] if "username" in user - ]: - self.proposal_membership[proposal_id] = True - else: - logger.info(f"Users not found in response: {response}") - self.proposal_membership[proposal_id] = False - return self.proposal_membership[proposal_id] - - def create_request_item(self, request) -> QtGui.QStandardItem: - col_item = QtGui.QStandardItem( - QtGui.QIcon(ICON), - request["request_obj"]["file_prefix"] - + "_" - + request["request_obj"]["protocol"], - ) - col_item.setData(request["uid"], 32) - col_item.setData("request", 33) - col_item.setFlags( - Qt.ItemFlag.ItemIsUserCheckable # type:ignore - | Qt.ItemFlag.ItemIsEnabled - | Qt.ItemFlag.ItemIsEditable - | Qt.ItemFlag.ItemIsSelectable - ) - if request["priority"] == 99999: - col_item.setCheckState(Qt.CheckState.Checked) - col_item.setBackground(QtGui.QColor("green")) - self.parent.refreshCollectionParams(request, validate_hdf5=False) - elif request["priority"] > 0: - col_item.setCheckState(Qt.CheckState.Checked) - col_item.setBackground(QtGui.QColor("white")) - elif request["priority"] < 0: - col_item.setCheckable(False) - col_item.setBackground(QtGui.QColor("cyan")) - else: - col_item.setCheckState(Qt.CheckState.Unchecked) - col_item.setBackground(QtGui.QColor("white")) - return col_item - - def refreshTreePriorityView( - self, - ): # "item" is a sample, "col_items" are requests which are children of samples. - collectionRunning = False - selectedIndex = None - mountedIndex = None - selectedSampleIndex = None - self.model.clear() - self.orderedRequests = db_lib.getOrderedRequestList(daq_utils.beamline) - dewarContents = db_lib.getContainerByName( - daq_utils.primaryDewarName, daq_utils.beamline - )["content"] - maxPucks = len(dewarContents) - requestedSampleList = [] - mountedPin = self.parent.mountedPin_pv.get() - for i in range( - len(self.orderedRequests) - ): # I need a list of samples for parent nodes - if self.orderedRequests[i]["sample"] not in requestedSampleList: - requestedSampleList.append(self.orderedRequests[i]["sample"]) - for i in range(len(requestedSampleList)): - sample = db_lib.getSampleByID(requestedSampleList[i]) - owner = sample["owner"] - parentItem = self.model.invisibleRootItem() - nodeString = str(db_lib.getSampleNamebyID(requestedSampleList[i])) - item = QtGui.QStandardItem( - QtGui.QIcon(ICON), - nodeString, - ) - item.setData(requestedSampleList[i], 32) - item.setData("sample", 33) - if requestedSampleList[i] == mountedPin: - self.set_mounted_sample(item) - parentItem.appendRow(item) - if requestedSampleList[i] == mountedPin: - mountedIndex = self.model.indexFromItem(item) - if ( - requestedSampleList[i] == self.parent.selectedSampleID - ): # looking for the selected item - selectedSampleIndex = self.model.indexFromItem(item) - parentItem = item - for k in range(len(self.orderedRequests)): - if self.orderedRequests[k]["sample"] == requestedSampleList[i]: - col_item = QtGui.QStandardItem( - QtGui.QIcon(ICON), - self.orderedRequests[k]["request_obj"]["file_prefix"] - + "_" - + self.orderedRequests[k]["request_obj"]["protocol"], - ) - col_item.setData(self.orderedRequests[k]["uid"], 32) - col_item.setData("request", 33) - col_item.setFlags( - Qt.ItemIsUserCheckable - | Qt.ItemIsEnabled - | Qt.ItemIsEditable - | Qt.ItemIsSelectable - ) - if self.orderedRequests[k]["priority"] == 99999: - col_item.setCheckState(Qt.Checked) - col_item.setBackground(QtGui.QColor("green")) - collectionRunning = True - self.parent.refreshCollectionParams( - self.orderedRequests[k], validate_hdf5=False - ) - - elif self.orderedRequests[k]["priority"] > 0: - col_item.setCheckState(Qt.Checked) - col_item.setBackground(QtGui.QColor("white")) - elif self.orderedRequests[k]["priority"] < 0: - col_item.setCheckable(False) - col_item.setBackground(QtGui.QColor("cyan")) - else: - col_item.setCheckState(Qt.Unchecked) - col_item.setBackground(QtGui.QColor("white")) - item.appendRow(col_item) - if ( - self.orderedRequests[k]["uid"] == self.parent.SelectedItemData - ): # looking for the selected item - selectedIndex = self.model.indexFromItem(col_item) - self.setModel(self.model) - if selectedSampleIndex != None and collectionRunning == False: - self.setCurrentIndex(selectedSampleIndex) - self.parent.row_clicked(selectedSampleIndex) - elif selectedSampleIndex == None and collectionRunning == False: - if mountedIndex != None: - self.setCurrentIndex(mountedIndex) - self.parent.row_clicked(mountedIndex) - else: - pass - - if selectedIndex != None and collectionRunning == False: - self.setCurrentIndex(selectedIndex) - self.parent.row_clicked(selectedIndex) - self.scrollTo(self.currentIndex(), QtWidgets.QAbstractItemView.PositionAtCenter) - self.expandAll() - - def queueSelectedSample(self, item): - if item.data(33) == "request": - reqID = str(item.data(32)) - checkedSampleRequest = db_lib.getRequestByID(reqID) # line not needed??? - if item.checkState() == Qt.Checked: - db_lib.updatePriority(reqID, 5000) - else: - db_lib.updatePriority(reqID, 0) - item.setBackground(QtGui.QColor("white")) - self.parent.treeChanged_pv.put( - self.parent.processID - ) # the idea is touch the pv, but have this gui instance not refresh - - def queueAllSelectedCB(self): - selmod = self.selectionModel() - selection = selmod.selection() - indexes = selection.indexes() - for i in range(len(indexes)): - item = self.model.itemFromIndex(indexes[i]) - itemData = str(item.data(32)) - itemDataType = str(item.data(33)) - if (itemDataType == "request") and item.isCheckable(): - selectedSampleRequest = db_lib.getRequestByID(itemData) - db_lib.updatePriority(itemData, 5000) - self.parent.treeChanged_pv.put(1) - - def deQueueAllSelectedCB(self): - selmod = self.selectionModel() - selection = selmod.selection() - indexes = selection.indexes() - for i in range(len(indexes)): - item = self.model.itemFromIndex(indexes[i]) - itemData = str(item.data(32)) - itemDataType = str(item.data(33)) - if (itemDataType == "request") and item.isCheckable(): - selectedSampleRequest = db_lib.getRequestByID(itemData) - db_lib.updatePriority(itemData, 0) - self.parent.treeChanged_pv.put(1) - - def confirmDelete(self, numReq): - if numReq: - quit_msg = f"Are you sure you want to delete {numReq} requests?" - else: - quit_msg = "Are you sure you want to delete all requests?" - self.parent.timerSample.stop() - reply = QtWidgets.QMessageBox.question( - self, - "Message", - quit_msg, - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No, - ) - self.parent.timerSample.start(SAMPLE_TIMER_DELAY) - if reply == QtWidgets.QMessageBox.Yes: - return 1 - else: - return 0 - - def deleteSelectedCB(self, deleteAll): - if deleteAll: - if not self.confirmDelete(0): - return - self.selectAll() - selmod = self.selectionModel() - selection = selmod.selection() - indexes = selection.indexes() - if len(indexes) > 1: - if not self.confirmDelete(len(indexes)): - return - progressInc = 100.0 / float(len(indexes)) - self.parent.progressDialog.setWindowTitle("Deleting Requests") - self.parent.progressDialog.show() - for i in range(len(indexes)): - self.parent.progressDialog.setValue(int((i + 1) * progressInc)) - item = self.model.itemFromIndex(indexes[i]) - itemData = str(item.data(32)) - itemDataType = str(item.data(33)) - if itemDataType == "request": - selectedSampleRequest = db_lib.getRequestByID(itemData) - self.selectedSampleID = selectedSampleRequest["sample"] - db_lib.deleteRequest(selectedSampleRequest["uid"]) - if selectedSampleRequest["request_obj"]["protocol"] in ( - "raster", - "stepRaster", - "multiCol", - ): - for i in range(len(self.parent.rasterList)): - if self.parent.rasterList[i] != None: - if ( - self.parent.rasterList[i]["uid"] - == selectedSampleRequest["uid"] - ): - self.parent.scene.removeItem( - self.parent.rasterList[i]["graphicsItem"] - ) - self.parent.rasterList[i] = None - if ( - selectedSampleRequest["request_obj"]["protocol"] == "vector" - or selectedSampleRequest["request_obj"]["protocol"] == "stepVector" - ): - self.parent.clearVectorCB() - self.parent.progressDialog.close() - self.parent.treeChanged_pv.put(1) - - def expandAllCB(self): - self.expandAll() - - def collapseAllCB(self): - self.collapseAll() - - def getSelectedSample(self): - selectedSampleID = None - if self.selectedIndexes(): - index = self.selectedIndexes()[0] - item = self.model.itemFromIndex(index) - if str(item.data(33)) == "sample": - selectedSampleID = str(item.data(32)) - elif str(item.data(33)) == "request": - selectedSampleRequest = db_lib.getRequestByID(item.data(32)) - selectedSampleID = selectedSampleRequest["sample"] - return selectedSampleID diff --git a/gui/dialog/__init__.py b/gui/dialog/__init__.py deleted file mode 100644 index 4a490c57..00000000 --- a/gui/dialog/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .snap_comment import SnapCommentDialog -from .raster_explore import RasterExploreDialog -from .staff_screen import StaffScreenDialog -from .user_screen import UserScreenDialog -from .puck_dialog import PuckDialog -from .dewar import DewarDialog -from .screen_defaults import ScreenDefaultsDialog diff --git a/gui/dialog/dewar.py b/gui/dialog/dewar.py deleted file mode 100644 index 016cb4a5..00000000 --- a/gui/dialog/dewar.py +++ /dev/null @@ -1,92 +0,0 @@ -import functools -import logging -import typing - -from qtpy import QtWidgets - -import daq_utils -import db_lib -from config_params import DEWAR_SECTORS, PUCKS_PER_DEWAR_SECTOR - -if typing.TYPE_CHECKING: - from lsdcGui import ControlMain - -logger = logging.getLogger() - - -class DewarDialog(QtWidgets.QDialog): - def __init__(self, parent: "ControlMain", action="add"): - super(DewarDialog, self).__init__(parent) - self.pucksPerDewarSector = PUCKS_PER_DEWAR_SECTOR[daq_utils.beamline] - self.dewarSectors = DEWAR_SECTORS[daq_utils.beamline] - self.action = action - self.parent = parent - - self.initData() - self.initUI() - - def initData(self): - dewarObj = db_lib.getPrimaryDewar(daq_utils.beamline) - puckLocs = dewarObj["content"] - self.data = [] - self.dewarPos = None - for i in range(len(puckLocs)): - if puckLocs[i] != "": - owner = db_lib.getContainerByID(puckLocs[i])["owner"] - self.data.append(db_lib.getContainerNameByID(puckLocs[i])) - else: - self.data.append("Empty") - logger.info(self.data) - - def initUI(self): - layout = QtWidgets.QVBoxLayout() - headerLabelLayout = QtWidgets.QHBoxLayout() - aLabel = QtWidgets.QLabel("A") - aLabel.setFixedWidth(15) - headerLabelLayout.addWidget(aLabel) - bLabel = QtWidgets.QLabel("B") - bLabel.setFixedWidth(10) - headerLabelLayout.addWidget(bLabel) - cLabel = QtWidgets.QLabel("C") - cLabel.setFixedWidth(10) - headerLabelLayout.addWidget(cLabel) - layout.addLayout(headerLabelLayout) - self.allButtonList = [None] * (self.dewarSectors * self.pucksPerDewarSector) - for i in range(0, self.dewarSectors): - rowLayout = QtWidgets.QHBoxLayout() - numLabel = QtWidgets.QLabel(str(i + 1)) - rowLayout.addWidget(numLabel) - for j in range(0, self.pucksPerDewarSector): - dataIndex = (i * self.pucksPerDewarSector) + j - self.allButtonList[dataIndex] = QtWidgets.QPushButton( - '{}: {}'.format(str(dataIndex+1),str(self.data[dataIndex])) - ) - self.allButtonList[dataIndex].clicked.connect( - functools.partial(self.on_button, str(dataIndex)) - ) - rowLayout.addWidget(self.allButtonList[dataIndex]) - layout.addLayout(rowLayout) - cancelButton = QtWidgets.QPushButton("Done") - cancelButton.clicked.connect(self.containerCancelCB) - layout.addWidget(cancelButton) - self.setLayout(layout) - - def on_button(self, n): - if self.action == "remove": - self.dewarPos = n - db_lib.removePuckFromDewar(daq_utils.beamline, int(n)) - self.allButtonList[int(n)].setText("Empty") - self.parent.treeChanged_pv.put(1) - else: - self.dewarPos = n - self.accept() - - def containerCancelCB(self): - self.dewarPos = 0 - self.reject() - - @staticmethod - def getDewarPos(parent=None, action="add"): - dialog = DewarDialog(parent, action) - result = dialog.exec_() - return (dialog.dewarPos, result == QtWidgets.QDialog.Accepted) diff --git a/gui/dialog/puck_dialog.py b/gui/dialog/puck_dialog.py deleted file mode 100644 index 5d257f7b..00000000 --- a/gui/dialog/puck_dialog.py +++ /dev/null @@ -1,93 +0,0 @@ -import logging -import typing - -from qtpy import QtCore, QtGui, QtWidgets -from qtpy.QtCore import Qt - -import daq_utils -import db_lib - -if typing.TYPE_CHECKING: - from lsdcGui import ControlMain - -logger = logging.getLogger() - - -class PuckDialog(QtWidgets.QDialog): - def __init__(self, parent: "ControlMain"): - super(PuckDialog, self).__init__(parent) - self.initData() - self.initUI() - - def initData(self): - puckListUnsorted = db_lib.getAllPucks(daq_utils.owner) - puckList = sorted(puckListUnsorted, key=lambda i: i["name"], reverse=False) - dewarObj = db_lib.getPrimaryDewar(daq_utils.beamline) - pucksInDewar = set(dewarObj["content"]) - self.model = QtGui.QStandardItemModel(self) - self.proxyModel = QtCore.QSortFilterProxyModel(self) - labels = ["Name"] - self.model.setHorizontalHeaderLabels(labels) - self.puckName = None - for puck in puckList: - if puck["uid"] not in pucksInDewar: - item = QtGui.QStandardItem(puck["name"]) - # Adding meta data to the puck. Each piece of meta data is identified using - # an int value, in this case is Qt.UserRole for puck modified time. This metadata is used - # to sort pucks - item.setData(puck.get("modified_time", 0), Qt.UserRole) - self.model.appendRow(item) - - def initUI(self): - self.tv = QtWidgets.QListView(self) - - self.tv.doubleClicked[QtCore.QModelIndex].connect(self.containerOKCB) - behavior = QtWidgets.QAbstractItemView.SelectRows - self.tv.setSelectionBehavior(behavior) - self.proxyModel.setSourceModel(self.model) - self.proxyModel.setSortRole(Qt.UserRole) - self.proxyModel.sort(0, order=Qt.DescendingOrder) - self.tv.setModel(self.proxyModel) - self.label = QtWidgets.QLabel(self) - self.buttons = QtWidgets.QDialogButtonBox( - QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, - Qt.Horizontal, - self, - ) - self.buttons.buttons()[0].clicked.connect(self.containerOKCB) - self.buttons.buttons()[1].clicked.connect(self.containerCancelCB) - self.searchBox = QtWidgets.QLineEdit(self) - self.searchBox.setPlaceholderText("Filter pucks...") - self.searchBox.textChanged.connect(self.filterPucks) - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.searchBox) - layout.addWidget(self.tv) - layout.addWidget(self.label) - layout.addWidget(self.buttons) - self.setLayout(layout) - - def filterPucks(self, a0: str): - self.proxyModel.setFilterFixedString(a0) - - def containerOKCB(self): - indexes = self.tv.selectedIndexes() - if indexes: - text = indexes[0].data() - self.label.setText(text) - self.puckName = text - self.accept() - else: - text = "" - self.puckName = text - self.reject() - - def containerCancelCB(self): - text = "" - self.reject() - self.puckName = text - - @staticmethod - def getPuckName(parent=None): - dialog = PuckDialog(parent) - result = dialog.exec_() - return (dialog.puckName, result == QtWidgets.QDialog.Accepted) diff --git a/gui/dialog/raster_explore.py b/gui/dialog/raster_explore.py deleted file mode 100644 index e86f79e0..00000000 --- a/gui/dialog/raster_explore.py +++ /dev/null @@ -1,53 +0,0 @@ -from qtpy import QtWidgets -from qtpy.QtCore import Qt - - -class RasterExploreDialog(QtWidgets.QDialog): - def __init__(self): - QtWidgets.QDialog.__init__(self) - self.setModal(False) - self.setWindowTitle("Raster Explore") - vBoxParams1 = QtWidgets.QVBoxLayout() - hBoxParams1 = QtWidgets.QHBoxLayout() - hBoxParams2 = QtWidgets.QHBoxLayout() - hBoxParams3 = QtWidgets.QHBoxLayout() - spotCountLabel = QtWidgets.QLabel("Spot Count:") - spotCountLabel.setFixedWidth(120) - self.spotCount_ledit = QtWidgets.QLabel() - self.spotCount_ledit.setFixedWidth(60) - hBoxParams1.addWidget(spotCountLabel) - hBoxParams1.addWidget(self.spotCount_ledit) - intensityLabel = QtWidgets.QLabel("Total Intensity:") - intensityLabel.setFixedWidth(120) - self.intensity_ledit = QtWidgets.QLabel() - self.intensity_ledit.setFixedWidth(60) - hBoxParams2.addWidget(intensityLabel) - hBoxParams2.addWidget(self.intensity_ledit) - resoLabel = QtWidgets.QLabel("Resolution:") - resoLabel.setFixedWidth(120) - self.reso_ledit = QtWidgets.QLabel() - self.reso_ledit.setFixedWidth(60) - hBoxParams3.addWidget(resoLabel) - hBoxParams3.addWidget(self.reso_ledit) - - self.buttons = QtWidgets.QDialogButtonBox( - QtWidgets.QDialogButtonBox.Cancel, Qt.Horizontal, self - ) - self.buttons.buttons()[0].clicked.connect(self.rasterExploreCancelCB) - vBoxParams1.addLayout(hBoxParams1) - vBoxParams1.addLayout(hBoxParams2) - vBoxParams1.addLayout(hBoxParams3) - vBoxParams1.addWidget(self.buttons) - self.setLayout(vBoxParams1) - - def setSpotCount(self, val): - self.spotCount_ledit.setText(str(val)) - - def setTotalIntensity(self, val): - self.intensity_ledit.setText(str(val)) - - def setResolution(self, val): - self.reso_ledit.setText(str(val)) - - def rasterExploreCancelCB(self): - self.done(QtWidgets.QDialog.Rejected) diff --git a/gui/dialog/screen_defaults.py b/gui/dialog/screen_defaults.py deleted file mode 100644 index a5f548c8..00000000 --- a/gui/dialog/screen_defaults.py +++ /dev/null @@ -1,274 +0,0 @@ -import logging -import typing - -from qtpy import QtCore, QtGui, QtWidgets - -import db_lib -from config_params import ( - RASTER_DOZOR_SPOT_LEVEL, - RASTER_TUNE_HIGH_RES, - RASTER_TUNE_ICE_RING_FLAG, - RASTER_TUNE_ICE_RING_WIDTH, - RASTER_TUNE_LOW_RES, - RASTER_TUNE_RESO_FLAG, - VALID_EXP_TIMES, -) -from daq_utils import beamline, getBlConfig, setBlConfig - -if typing.TYPE_CHECKING: - from lsdcGui import ControlMain - -logger = logging.getLogger() - - -class ScreenDefaultsDialog(QtWidgets.QDialog): - def __init__(self, parent: "ControlMain"): - QtWidgets.QDialog.__init__(self, parent) - self.parent = parent - self.setModal(False) - self.setWindowTitle("Raster Params") - - vBoxColParams1 = QtWidgets.QVBoxLayout() - - collectionGB = QtWidgets.QGroupBox() - collectionGB.setTitle("Collection parameters") - - hBoxColParams2 = QtWidgets.QHBoxLayout() - colRangeLabel = QtWidgets.QLabel("Oscillation Width:") - colRangeLabel.setAlignment(QtCore.Qt.AlignCenter) - self.osc_range_ledit = ( - QtWidgets.QLineEdit() - ) # note, this is for rastering! same name used for data collections - self.setGuiValues({"osc_range": getBlConfig("rasterDefaultWidth")}) - self.osc_range_ledit.returnPressed.connect(self.screenDefaultsOKCB) - colExptimeLabel = QtWidgets.QLabel("ExposureTime:") - colExptimeLabel.setAlignment(QtCore.Qt.AlignCenter) - self.exp_time_ledit = QtWidgets.QLineEdit() - self.setGuiValues({"exp_time": getBlConfig("rasterDefaultTime")}) - self.exp_time_ledit.returnPressed.connect(self.screenDefaultsOKCB) - self.exp_time_ledit.setValidator( - QtGui.QDoubleValidator( - VALID_EXP_TIMES[beamline]["min"], - VALID_EXP_TIMES[beamline]["max"], - VALID_EXP_TIMES[beamline]["digits"], - ) - ) - self.exp_time_ledit.textChanged.connect(self.checkEntryState) - - colTransLabel = QtWidgets.QLabel("Transmission (0.0-1.0):") - colTransLabel.setAlignment(QtCore.Qt.AlignCenter) - self.trans_ledit = QtWidgets.QLineEdit() - self.setGuiValues({"transmission": getBlConfig("rasterDefaultTrans")}) - self.trans_ledit.returnPressed.connect(self.screenDefaultsOKCB) - hBoxColParams2.addWidget(colRangeLabel) - hBoxColParams2.addWidget(self.osc_range_ledit) - hBoxColParams2.addWidget(colExptimeLabel) - hBoxColParams2.addWidget(self.exp_time_ledit) - hBoxColParams2.addWidget(colTransLabel) - hBoxColParams2.addWidget(self.trans_ledit) - collectionGB.setLayout(hBoxColParams2) - - dozorGB = QtWidgets.QGroupBox() - dozorGB.setTitle("Dozor Parameter") - hBoxColParams2a = QtWidgets.QHBoxLayout() - dozorSpotLevelLabel = QtWidgets.QLabel( - "Dozor Spot Level\n(Applies immediately)" - ) - self.dozorSpotLevel = QtWidgets.QComboBox() - self.dozorSpotLevel.addItems(["5", "6", "7", "8"]) - self.dozorSpotLevel.currentIndexChanged.connect(self.dozorSpotLevelChangedCB) - hBoxColParams2a.addWidget(dozorSpotLevelLabel) - hBoxColParams2a.addWidget(self.dozorSpotLevel) - dozorGB.setLayout(hBoxColParams2a) - - dialsGB = QtWidgets.QGroupBox() - dialsGB.setTitle("Dials Parameters") - vBoxDialsParams = QtWidgets.QVBoxLayout() - hBoxColParams2b = QtWidgets.QHBoxLayout() - colMinSpotLabel = QtWidgets.QLabel("Min Spot Size:") - colMinSpotLabel.setAlignment(QtCore.Qt.AlignCenter) - self.minSpot_ledit = QtWidgets.QLineEdit() - self.minSpot_ledit.setText(str(getBlConfig("rasterDefaultMinSpotSize"))) - self.minSpot_ledit.returnPressed.connect(self.screenDefaultsOKCB) - hBoxColParams2b.addWidget(colMinSpotLabel) - hBoxColParams2b.addWidget(self.minSpot_ledit) - - self.hBoxRasterLayout2 = QtWidgets.QHBoxLayout() - rasterTuneLabel = QtWidgets.QLabel("Raster\nTuning") - self.rasterResoCheckBox = QtWidgets.QCheckBox("Constrain Resolution") - self.rasterResoCheckBox.stateChanged.connect(self.rasterResoCheckCB) - rasterLowResLabel = QtWidgets.QLabel("LowRes:") - self.rasterLowRes = QtWidgets.QLineEdit() - self.rasterLowRes.setText(str(getBlConfig(RASTER_TUNE_LOW_RES))) - self.rasterLowRes.returnPressed.connect(self.screenDefaultsOKCB) - rasterHighResLabel = QtWidgets.QLabel("HighRes:") - self.rasterHighRes = QtWidgets.QLineEdit() - self.rasterHighRes.setText(str(getBlConfig(RASTER_TUNE_HIGH_RES))) - self.rasterHighRes.returnPressed.connect(self.screenDefaultsOKCB) - if getBlConfig(RASTER_TUNE_RESO_FLAG) == 1: - resoFlag = True - else: - resoFlag = False - self.rasterHighRes.setEnabled(False) - self.rasterLowRes.setEnabled(False) - self.rasterResoCheckBox.setChecked(resoFlag) - self.rasterIceRingCheckBox = QtWidgets.QCheckBox("Ice Ring") - self.rasterIceRingCheckBox.setChecked(False) - self.rasterIceRingCheckBox.stateChanged.connect(self.rasterIceRingCheckCB) - self.rasterIceRingWidth = QtWidgets.QLineEdit() - self.rasterIceRingWidth.setText(str(getBlConfig(RASTER_TUNE_ICE_RING_WIDTH))) - self.rasterIceRingWidth.returnPressed.connect(self.screenDefaultsOKCB) - self.rasterIceRingWidth.setEnabled(False) - if getBlConfig(RASTER_TUNE_ICE_RING_FLAG) == 1: - iceRingFlag = True - else: - iceRingFlag = False - self.rasterIceRingCheckBox.setChecked(iceRingFlag) - self.hBoxRasterLayout2.addWidget(self.rasterResoCheckBox) - self.hBoxRasterLayout2.addWidget(rasterLowResLabel) - self.hBoxRasterLayout2.addWidget(self.rasterLowRes) - self.hBoxRasterLayout2.addWidget(rasterHighResLabel) - self.hBoxRasterLayout2.addWidget(self.rasterHighRes) - self.hBoxRasterLayout2.addWidget(self.rasterIceRingCheckBox) - self.hBoxRasterLayout2.addWidget(self.rasterIceRingWidth) - - self.hBoxRasterLayout3 = QtWidgets.QHBoxLayout() - self.rasterThreshCheckBox = QtWidgets.QCheckBox("Tune Threshold") - if getBlConfig("rasterThreshFlag") == 1: - threshFlag = True - else: - threshFlag = False - self.rasterThreshCheckBox.setChecked(threshFlag) - self.rasterThreshCheckBox.stateChanged.connect(self.rasterThreshCheckCB) - - rasterThreshKernSizeLabel = QtWidgets.QLabel("KernelSize") - self.rasterThreshKernSize = QtWidgets.QLineEdit() - self.rasterThreshKernSize.setText(str(getBlConfig("rasterThreshKernSize"))) - self.rasterThreshKernSize.returnPressed.connect(self.screenDefaultsOKCB) - rasterThreshSigBckLabel = QtWidgets.QLabel("SigmaBkrnd") - self.rasterThreshSigBckrnd = QtWidgets.QLineEdit() - self.rasterThreshSigBckrnd.setText(str(getBlConfig("rasterThreshSigBckrnd"))) - self.rasterThreshSigBckrnd.returnPressed.connect(self.screenDefaultsOKCB) - rasterThreshSigStrongLabel = QtWidgets.QLabel("SigmaStrong") - self.rasterThreshSigStrong = QtWidgets.QLineEdit() - self.rasterThreshSigStrong.setText(str(getBlConfig("rasterThreshSigStrong"))) - self.rasterThreshSigStrong.returnPressed.connect(self.screenDefaultsOKCB) - self.rasterThreshKernSize.setEnabled(threshFlag) - self.rasterThreshSigBckrnd.setEnabled(threshFlag) - self.rasterThreshSigStrong.setEnabled(threshFlag) - self.hBoxRasterLayout3.addWidget(self.rasterThreshCheckBox) - self.hBoxRasterLayout3.addWidget(rasterThreshKernSizeLabel) - self.hBoxRasterLayout3.addWidget(self.rasterThreshKernSize) - self.hBoxRasterLayout3.addWidget(rasterThreshSigBckLabel) - self.hBoxRasterLayout3.addWidget(self.rasterThreshSigBckrnd) - self.hBoxRasterLayout3.addWidget(rasterThreshSigStrongLabel) - self.hBoxRasterLayout3.addWidget(self.rasterThreshSigStrong) - - vBoxDialsParams.addLayout(hBoxColParams2b) - vBoxDialsParams.addLayout(self.hBoxRasterLayout2) - vBoxDialsParams.addLayout(self.hBoxRasterLayout3) - dialsGB.setLayout(vBoxDialsParams) - - reprocessRasterButton = QtWidgets.QPushButton("ReProcessRaster") - reprocessRasterButton.clicked.connect(self.reprocessRasterRequestCB) - self.buttons = QtWidgets.QDialogButtonBox( - QtWidgets.QDialogButtonBox.Apply | QtWidgets.QDialogButtonBox.Cancel, - QtCore.Qt.Horizontal, - self, - ) - self.buttons.buttons()[1].clicked.connect(self.screenDefaultsOKCB) - self.buttons.buttons()[0].clicked.connect(self.screenDefaultsCancelCB) - vBoxColParams1.addWidget(collectionGB) - vBoxColParams1.addWidget(dozorGB) - vBoxColParams1.addWidget(dialsGB) - # vBoxColParams1.addWidget(reprocessRasterButton) - vBoxColParams1.addWidget(self.buttons) - self.setLayout(vBoxColParams1) - - def setGuiValues(self, values): - for item, value in values.items(): - logger.info("resetting %s to %s" % (item, value)) - if item == "osc_range": - self.osc_range_ledit.setText("%.3f" % float(value)) - elif item == "exp_time": - self.exp_time_ledit.setText("%.3f" % float(value)) - elif item == "transmission": - self.trans_ledit.setText("%.3f" % float(value)) - else: - logger.error("setGuiValues unknown item: %s value: %s" % (item, value)) - - def reprocessRasterRequestCB(self): - self.parent.eraseCB() - try: - reqID = self.parent.selectedSampleRequest["uid"] - self.parent.drawPolyRaster(db_lib.getRequestByID(reqID)) - self.parent.send_to_server('reprocessRaster("' + str(reqID) + '")') - except: - pass - - def screenDefaultsCancelCB(self): - self.done(QtWidgets.QDialog.Rejected) - - def dozorSpotLevelChangedCB(self, i): - setBlConfig(RASTER_DOZOR_SPOT_LEVEL, int(self.dozorSpotLevel.itemText(i))) - - def screenDefaultsOKCB(self): - setBlConfig("rasterDefaultWidth", float(self.osc_range_ledit.text())) - setBlConfig("rasterDefaultTime", float(self.exp_time_ledit.text())) - setBlConfig("rasterDefaultTrans", float(self.trans_ledit.text())) - setBlConfig("rasterDefaultMinSpotSize", float(self.minSpot_ledit.text())) - setBlConfig(RASTER_TUNE_LOW_RES, float(self.rasterLowRes.text())) - setBlConfig(RASTER_TUNE_HIGH_RES, float(self.rasterHighRes.text())) - setBlConfig(RASTER_TUNE_ICE_RING_WIDTH, float(self.rasterIceRingWidth.text())) - setBlConfig("rasterThreshKernSize", float(self.rasterThreshKernSize.text())) - setBlConfig("rasterThreshSigBckrnd", float(self.rasterThreshSigBckrnd.text())) - setBlConfig("rasterThreshSigStrong", float(self.rasterThreshSigStrong.text())) - if self.rasterIceRingCheckBox.isChecked(): - setBlConfig(RASTER_TUNE_ICE_RING_FLAG, 1) - else: - setBlConfig(RASTER_TUNE_ICE_RING_FLAG, 0) - if self.rasterResoCheckBox.isChecked(): - setBlConfig(RASTER_TUNE_RESO_FLAG, 1) - else: - setBlConfig(RASTER_TUNE_RESO_FLAG, 0) - - def rasterIceRingCheckCB(self, state): - if state == QtCore.Qt.Checked: - self.rasterIceRingWidth.setEnabled(True) - else: - self.rasterIceRingWidth.setEnabled(False) - - def rasterResoCheckCB(self, state): - if state == QtCore.Qt.Checked: - setBlConfig(RASTER_TUNE_RESO_FLAG, 1) - self.rasterLowRes.setEnabled(True) - self.rasterHighRes.setEnabled(True) - else: - setBlConfig(RASTER_TUNE_RESO_FLAG, 0) - self.rasterLowRes.setEnabled(False) - self.rasterHighRes.setEnabled(False) - - def rasterThreshCheckCB(self, state): - if state == QtCore.Qt.Checked: - setBlConfig("rasterThreshFlag", 1) - self.rasterThreshKernSize.setEnabled(True) - self.rasterThreshSigBckrnd.setEnabled(True) - self.rasterThreshSigStrong.setEnabled(True) - else: - setBlConfig("rasterThreshFlag", 0) - self.rasterThreshKernSize.setEnabled(False) - self.rasterThreshSigBckrnd.setEnabled(False) - self.rasterThreshSigStrong.setEnabled(False) - - # code below and its application from: https://snorfalorpagus.net/blog/2014/08/09/validating-user-input-in-pyqt4-using-qvalidator/ - def checkEntryState(self, *args, **kwargs): - sender = self.sender() - validator = sender.validator() - state = validator.validate(sender.text(), 0)[0] - if state == QtGui.QValidator.Intermediate: - color = "#fff79a" # yellow - elif state == QtGui.QValidator.Invalid: - color = "#f6989d" # red - else: - color = "#ffffff" # white - sender.setStyleSheet("QLineEdit { background-color: %s }" % color) diff --git a/gui/dialog/snap_comment.py b/gui/dialog/snap_comment.py deleted file mode 100644 index c97a17b1..00000000 --- a/gui/dialog/snap_comment.py +++ /dev/null @@ -1,40 +0,0 @@ -from qtpy import QtWidgets - - -class SnapCommentDialog(QtWidgets.QDialog): - def __init__(self, parent=None): - QtWidgets.QDialog.__init__(self, parent) - self.setWindowTitle("Snapshot Comment") - self.setModal(False) - vBoxColParams1 = QtWidgets.QVBoxLayout() - hBoxColParams1 = QtWidgets.QHBoxLayout() - self.textEdit = QtWidgets.QPlainTextEdit() - vBoxColParams1.addWidget(self.textEdit) - self.ologCheckBox = QtWidgets.QCheckBox("Save to Olog") - self.ologCheckBox.setChecked(False) - vBoxColParams1.addWidget(self.ologCheckBox) - commentButton = QtWidgets.QPushButton("Add Comment") - commentButton.clicked.connect(self.commentCB) - cancelButton = QtWidgets.QPushButton("Cancel") - cancelButton.clicked.connect(self.cancelCB) - - hBoxColParams1.addWidget(commentButton) - hBoxColParams1.addWidget(cancelButton) - vBoxColParams1.addLayout(hBoxColParams1) - self.setLayout(vBoxColParams1) - - def cancelCB(self): - self.comment = "" - self.useOlog = False - self.reject() - - def commentCB(self): - self.comment = self.textEdit.toPlainText() - self.useOlog = self.ologCheckBox.isChecked() - self.accept() - - @staticmethod - def getComment(parent=None): - dialog = SnapCommentDialog(parent) - result = dialog.exec_() - return (dialog.comment, dialog.useOlog, result == QtWidgets.QDialog.Accepted) diff --git a/gui/dialog/staff_screen.py b/gui/dialog/staff_screen.py deleted file mode 100644 index da40dc70..00000000 --- a/gui/dialog/staff_screen.py +++ /dev/null @@ -1,365 +0,0 @@ -import logging -import typing -import daq_utils - -from qtpy import QtCore, QtWidgets -from qtpy.QtWidgets import QCheckBox - -from config_params import BEAM_CHECK, TOP_VIEW_CHECK, UNMOUNT_COLD_CHECK -from daq_utils import getBlConfig, setBlConfig - -if typing.TYPE_CHECKING: - from lsdcGui import ControlMain - -logger = logging.getLogger() - - -class StaffScreenDialog(QtWidgets.QFrame): - def __init__(self, parent: "ControlMain", **kwargs): - show = kwargs.get("show", True) - self.parent = parent - QtWidgets.QFrame.__init__(self) - self.setWindowTitle("Staff Only") - self.spotNodeCount = 8 - self.fastDPNodeCount = 4 - self.cpuCount = 28 - vBoxColParams1 = QtWidgets.QVBoxLayout() - hBoxColParams0 = QtWidgets.QHBoxLayout() - hBoxColParams1 = QtWidgets.QHBoxLayout() - hBoxColParams2 = QtWidgets.QHBoxLayout() - hBoxColParams3 = QtWidgets.QHBoxLayout() - hBoxFastDP = QtWidgets.QHBoxLayout() - hBoxSpotfinder = QtWidgets.QHBoxLayout() - puckToDewarButton = QtWidgets.QPushButton("Puck to Dewar...") - puckToDewarButton.clicked.connect(self.parent.puckToDewarCB) - removePuckButton = QtWidgets.QPushButton("Remove Puck...") - removePuckButton.clicked.connect(self.parent.removePuckCB) - hBoxColParams0.addWidget(puckToDewarButton) - hBoxColParams0.addWidget(removePuckButton) - self.robotOnCheckBox = QCheckBox("Robot (On)") - if getBlConfig("robot_online") == 1: - self.robotOnCheckBox.setChecked(True) - else: - self.robotOnCheckBox.setChecked(False) - self.robotOnCheckBox.stateChanged.connect(self.robotOnCheckCB) - self.topViewCheckOnCheckBox = QCheckBox("TopViewCheck (On)") - if getBlConfig(TOP_VIEW_CHECK) == 1: - self.topViewCheckOnCheckBox.setChecked(True) - else: - self.topViewCheckOnCheckBox.setChecked(False) - self.topViewCheckOnCheckBox.stateChanged.connect(self.topViewOnCheckCB) - # BeamCheck check box - self.beamCheckOnCheckBox = QCheckBox("BeamCheck (On)") - if getBlConfig(BEAM_CHECK) == 1: - self.beamCheckOnCheckBox.setChecked(True) - else: - self.beamCheckOnCheckBox.setChecked(False) - self.beamCheckOnCheckBox.stateChanged.connect(self.beamCheckOnCheckCB) - - self.gripperUnmountColdCheckBox = QCheckBox("Unmount Cold") - self.gripperUnmountColdCheckBox.stateChanged.connect(self.unmountColdCheckCB) - if getBlConfig(UNMOUNT_COLD_CHECK) == 1: - self.gripperUnmountColdCheckBox.setEnabled(True) - self.gripperUnmountColdCheckBox.setChecked(True) - else: - self.gripperUnmountColdCheckBox.setEnabled(False) - self.gripperUnmountColdCheckBox.setChecked(False) - - self.queueCollectOnCheckBox = QCheckBox("Queue Collect") - hBoxColParams1.addWidget(self.queueCollectOnCheckBox) - if getBlConfig("queueCollect") == 1: - self.queueCollectOnCheckBox.setChecked(True) - self.gripperUnmountColdCheckBox.setEnabled(True) - self.parent.queue_collect_status_widget.setText("Queue Collect: ON") - else: - self.queueCollectOnCheckBox.setChecked(False) - self.gripperUnmountColdCheckBox.setEnabled(False) - self.parent.queue_collect_status_widget.setText("Queue Collect: OFF") - self.queueCollectOnCheckBox.stateChanged.connect(self.queueCollectOnCheckCB) - self.vertRasterOnCheckBox = QCheckBox("Vert. Raster") - hBoxColParams1.addWidget(self.vertRasterOnCheckBox) - if getBlConfig("vertRasterOn") == 1: - self.vertRasterOnCheckBox.setChecked(True) - else: - self.vertRasterOnCheckBox.setChecked(False) - self.vertRasterOnCheckBox.stateChanged.connect(self.vertRasterOnCheckCB) - self.procRasterOnCheckBox = QCheckBox("Process Raster") - hBoxColParams1.addWidget(self.procRasterOnCheckBox) - if getBlConfig("rasterProcessFlag") == 1: - self.procRasterOnCheckBox.setChecked(True) - else: - self.procRasterOnCheckBox.setChecked(False) - self.procRasterOnCheckBox.stateChanged.connect(self.procRasterOnCheckCB) - self.guiRemoteOnCheckBox = QCheckBox("GUI Remote") - hBoxColParams1.addWidget(self.guiRemoteOnCheckBox) - if getBlConfig("omegaMonitorPV") == "VAL": - self.guiRemoteOnCheckBox.setChecked(True) - else: - self.guiRemoteOnCheckBox.setChecked(False) - self.guiRemoteOnCheckBox.stateChanged.connect(self.guiRemoteOnCheckCB) - self.albulaDispCheckBox = QCheckBox("Display Data (Albula)") - self.albulaDispCheckBox.setChecked(True) - hBoxColParams1.addWidget(self.albulaDispCheckBox) - - self.enableMountCheckBox = QCheckBox("Enable Mount") - if getBlConfig("mountEnabled") == 1: - self.enableMountCheckBox.setChecked(True) - else: - self.enableMountCheckBox.setChecked(False) - self.enableMountCheckBox.stateChanged.connect(self.enableMountCheckCB) - self.unmountColdButton = QtWidgets.QPushButton("Unmount Cold") - self.unmountColdButton.clicked.connect(self.unmountColdCB) - self.openPort1Button = QtWidgets.QPushButton("Open Port 1") - self.openPort1Button.clicked.connect(self.openPort1CB) - self.closePortsButton = QtWidgets.QPushButton("Close Ports") - self.closePortsButton.clicked.connect(self.closePortsCB) - self.warmupButton = QtWidgets.QPushButton("Dry Gripper") - self.warmupButton.clicked.connect(self.parent.dryGripperCB) - self.enableTScreenButton = QtWidgets.QPushButton("Enable Dewar Tscreen") - self.enableTScreenButton.clicked.connect(self.parent.enableTScreenGripperCB) - self.parkButton = QtWidgets.QPushButton("Park Gripper") - self.parkButton.clicked.connect(self.parent.parkGripperCB) - self.homePinsButton = QtWidgets.QPushButton("Home Pins") - self.homePinsButton.clicked.connect(self.homePinsCB) - self.clearMountedSampleButton = QtWidgets.QPushButton("Clear Mounted Sample") - self.clearMountedSampleButton.clicked.connect(self.clearMountedSampleCB) - hBoxColParams2.addWidget(self.openPort1Button) - hBoxColParams2.addWidget(self.closePortsButton) - hBoxColParams2.addWidget(self.unmountColdButton) - hBoxColParams2.addWidget(self.warmupButton) - hBoxColParams2.addWidget(self.enableTScreenButton) - hBoxColParams2.addWidget(self.parkButton) - hBoxColParams2.addWidget(self.clearMountedSampleButton) - hBoxColParams1.addWidget(self.homePinsButton) - self.setFastDPNodesButton = QtWidgets.QPushButton("Set FastDP Nodes") - self.setFastDPNodesButton.clicked.connect(self.setFastDPNodesCB) - hBoxFastDP.addWidget(self.setFastDPNodesButton) - self.fastDPNodeEntryList = [] - nodeList = self.getFastDPNodeList() - for i in range(0, self.fastDPNodeCount): - self.fastDPNodeEntryList.append(QtWidgets.QLineEdit()) - self.fastDPNodeEntryList[i].setFixedWidth(30) - self.fastDPNodeEntryList[i].setText(str(nodeList[i])) - hBoxFastDP.addWidget(self.fastDPNodeEntryList[i]) - self.fastDPCheckBox = QCheckBox("FastDP") - self.fastDPCheckBox.setChecked(True) - hBoxFastDP.addWidget(self.fastDPCheckBox) - self.setBeamcenterButton = QtWidgets.QPushButton("Set Beamcenter") - self.setBeamcenterButton.clicked.connect(self.setBeamcenterCB) - hBoxFastDP.addWidget(self.setBeamcenterButton) - self.beamcenterX_ledit = QtWidgets.QLineEdit() - self.beamcenterX_ledit.setText(str(self.parent.beamCenterX_pv.get())) - self.beamcenterY_ledit = QtWidgets.QLineEdit() - self.beamcenterY_ledit.setText(str(self.parent.beamCenterY_pv.get())) - hBoxFastDP.addWidget(self.beamcenterX_ledit) - hBoxFastDP.addWidget(self.beamcenterY_ledit) - self.setSpotNodesButton = QtWidgets.QPushButton("Set Spotfinder Nodes") - self.setSpotNodesButton.clicked.connect(self.setSpotNodesCB) - self.lockGuiButton = QtWidgets.QPushButton("Lock") - self.lockGuiButton.clicked.connect(self.lockGuiCB) - self.unLockGuiButton = QtWidgets.QPushButton("unLock") - self.unLockGuiButton.clicked.connect(self.unLockGuiCB) - hBoxSpotfinder.addWidget(self.lockGuiButton) - hBoxSpotfinder.addWidget(self.unLockGuiButton) - hBoxSpotfinder.addWidget(self.setSpotNodesButton) - self.spotNodeEntryList = [] - nodeList = self.getSpotNodeList() - for i in range(0, self.spotNodeCount): - self.spotNodeEntryList.append(QtWidgets.QLineEdit()) - self.spotNodeEntryList[i].setFixedWidth(30) - self.spotNodeEntryList[i].setText(str(nodeList[i])) - hBoxSpotfinder.addWidget(self.spotNodeEntryList[i]) - robotGB = QtWidgets.QGroupBox() - robotGB.setTitle("Robot") - hBoxRobot1 = QtWidgets.QHBoxLayout() - vBoxRobot1 = QtWidgets.QVBoxLayout() - self.recoverRobotButton = QtWidgets.QPushButton("Recover Robot") - self.recoverRobotButton.clicked.connect(self.recoverRobotCB) - self.rebootEMBLButton = QtWidgets.QPushButton("Reboot EMBL") - self.rebootEMBLButton.clicked.connect(self.rebootEMBL_CB) - self.restartEMBLButton = QtWidgets.QPushButton("Start EMBL") - self.restartEMBLButton.clicked.connect(self.restartEMBL_CB) - self.openGripperButton = QtWidgets.QPushButton("Open Gripper") - self.openGripperButton.clicked.connect(self.openGripper_CB) - self.closeGripperButton = QtWidgets.QPushButton("Close Gripper") - self.closeGripperButton.clicked.connect(self.closeGripper_CB) - hBoxRobot1.addWidget(self.robotOnCheckBox) - hBoxRobot1.addWidget(self.beamCheckOnCheckBox) - hBoxRobot1.addWidget(self.gripperUnmountColdCheckBox) - hBoxRobot1.addWidget(self.topViewCheckOnCheckBox) - hBoxRobot1.addWidget(self.enableMountCheckBox) - hBoxRobot1.addWidget(self.recoverRobotButton) - hBoxRobot1.addWidget(self.rebootEMBLButton) - hBoxRobot1.addWidget(self.restartEMBLButton) - hBoxRobot1.addWidget(self.openGripperButton) - hBoxRobot1.addWidget(self.closeGripperButton) - vBoxRobot1.addLayout(hBoxRobot1) - vBoxRobot1.addLayout(hBoxColParams2) - robotGB.setLayout(vBoxRobot1) - self.buttons = QtWidgets.QDialogButtonBox( - QtWidgets.QDialogButtonBox.Ok, QtCore.Qt.Horizontal, self - ) - self.buttons.buttons()[0].clicked.connect(self.screenDefaultsOKCB) - vBoxColParams1.addLayout(hBoxColParams0) - vBoxColParams1.addLayout(hBoxColParams1) - vBoxColParams1.addLayout(hBoxFastDP) - vBoxColParams1.addLayout(hBoxSpotfinder) - vBoxColParams1.addWidget(robotGB) - vBoxColParams1.addWidget(self.buttons) - self.setLayout(vBoxColParams1) - if show: - self.show() - - def getSpotNodeList(self): - nodeList = [] - for i in range(0, self.spotNodeCount): - if daq_utils.beamline == 'nyx': - nodeList.append(int(getBlConfig("spotNode"+str(i+1)).split('epu')[1])) - else: - nodeList.append(int(getBlConfig("spotNode"+str(i+1)).split('cpu')[1])) - return nodeList - - def getFastDPNodeList(self): - nodeList = [] - for i in range(0, self.fastDPNodeCount): - nodeList.append(int(getBlConfig("fastDPNode" + str(i + 1)).split("cpu")[1])) - return nodeList - - def setFastDPNodesCB(self): - comm_s = "fastDPNodes(" - for i in range(0, self.fastDPNodeCount): - comm_s = comm_s + str(self.fastDPNodeEntryList[i].text()) - if i == self.fastDPNodeCount - 1: - comm_s = comm_s + ")" - else: - comm_s = comm_s + "," - logger.info(comm_s) - self.parent.send_to_server(comm_s) - - def lockGuiCB(self): - self.parent.send_to_server("lockControl") - - def unLockGuiCB(self): - self.parent.send_to_server("unlockControl") - - def setSpotNodesCB(self): - comm_s = "spotNodes(" - for i in range(0, self.spotNodeCount): - comm_s = comm_s + str(self.spotNodeEntryList[i].text()) - if i == self.spotNodeCount - 1: - comm_s = comm_s + ")" - else: - comm_s = comm_s + "," - logger.info(comm_s) - self.parent.send_to_server(comm_s) - - def unmountColdCB(self): - self.parent.send_to_server("unmountCold()") - - def openPort1CB(self): - self.parent.send_to_server("openPort(1)") - - def setBeamcenterCB(self): - self.parent.send_to_server( - "set_beamcenter (" - + str(self.beamcenterX_ledit.text()) - + "," - + str(self.beamcenterY_ledit.text()) - + ")" - ) - - def closePortsCB(self): - self.parent.send_to_server("closePorts()") - - def clearMountedSampleCB(self): - self.parent.send_to_server("clearMountedSample()") - - def recoverRobotCB(self): - self.parent.aux_send_to_server("recoverRobot()") - - def rebootEMBL_CB(self): - self.parent.aux_send_to_server("rebootEMBL()") - - def restartEMBL_CB(self): - self.parent.send_to_server("restartEMBL()") - - def openGripper_CB(self): - self.parent.send_to_server("openGripper()") - - def closeGripper_CB(self): - self.parent.send_to_server("closeGripper()") - - def homePinsCB(self): - self.parent.send_to_server("homePins()") - - def robotOnCheckCB(self, state): - if state == QtCore.Qt.Checked: - setBlConfig("robot_online", 1) - else: - setBlConfig("robot_online", 0) - - def beamCheckOnCheckCB(self, state): - if state == QtCore.Qt.Checked: - setBlConfig(BEAM_CHECK, 1) - logger.debug(f"{BEAM_CHECK} on") - else: - setBlConfig(BEAM_CHECK, 0) - logger.debug(f"{BEAM_CHECK} off") - - def unmountColdCheckCB(self, state): - if state == QtCore.Qt.Checked: - logger.info("unmountColdCheckCB On") - setBlConfig(UNMOUNT_COLD_CHECK, 1) - else: - logger.info("unmountColdCheckCB Off") - setBlConfig(UNMOUNT_COLD_CHECK, 0) - - def topViewOnCheckCB(self, state): - if state == QtCore.Qt.Checked: - setBlConfig(TOP_VIEW_CHECK, 1) - else: - setBlConfig(TOP_VIEW_CHECK, 0) - - def vertRasterOnCheckCB(self, state): - if state == QtCore.Qt.Checked: - setBlConfig("vertRasterOn", 1) - else: - setBlConfig("vertRasterOn", 0) - - def procRasterOnCheckCB(self, state): - if state == QtCore.Qt.Checked: - setBlConfig("rasterProcessFlag", 1) - else: - setBlConfig("rasterProcessFlag", 0) - - def guiRemoteOnCheckCB(self, state): - if state == QtCore.Qt.Checked: - setBlConfig("omegaMonitorPV", "VAL") - else: - setBlConfig("omegaMonitorPV", "RBV") - - def queueCollectOnCheckCB(self, state): - if state == QtCore.Qt.Checked: - setBlConfig("queueCollect", 1) - self.gripperUnmountColdCheckBox.setEnabled(True) - self.parent.queue_collect_status_widget.setText("Queue Collect: ON") - else: - setBlConfig("queueCollect", 0) - self.gripperUnmountColdCheckBox.setEnabled(False) - self.gripperUnmountColdCheckBox.setChecked(False) - self.parent.queue_collect_status_widget.setText("Queue Collect: OFF") - self.parent.row_clicked( - 0 - ) # This is so that appropriate boxes are filled when toggling queue collect - - def enableMountCheckCB(self, state): - if state == QtCore.Qt.Checked: - setBlConfig("mountEnabled", 1) - else: - setBlConfig("mountEnabled", 0) - - def screenDefaultsCancelCB(self): - self.hide() - - def screenDefaultsOKCB(self): - self.hide() diff --git a/gui/dialog/user_screen.py b/gui/dialog/user_screen.py deleted file mode 100644 index e4c0ef20..00000000 --- a/gui/dialog/user_screen.py +++ /dev/null @@ -1,256 +0,0 @@ -import logging -import typing - -from qt_epics.QtEpicsPVLabel import QtEpicsPVLabel -from qtpy import QtCore, QtWidgets -from qtpy.QtWidgets import QCheckBox - -import daq_utils - -if typing.TYPE_CHECKING: - from lsdcGui import ControlMain - -logger = logging.getLogger() - - -class UserScreenDialog(QtWidgets.QFrame): - def __init__(self, parent: "ControlMain"): - self.parent = parent - QtWidgets.QFrame.__init__(self) - self.setWindowTitle("User Extras") - vBoxColParams1 = QtWidgets.QVBoxLayout() - hBoxColParams1 = QtWidgets.QHBoxLayout() - hBoxColParams2 = QtWidgets.QHBoxLayout() - hBoxColParams25 = QtWidgets.QHBoxLayout() - hBoxColParams3 = QtWidgets.QHBoxLayout() - govLabel = QtWidgets.QLabel("Set Governor State:") - self.SEbutton = QtWidgets.QPushButton("SE") - self.SEbutton.clicked.connect(self.SEgovCB) - self.SAbutton = QtWidgets.QPushButton("SA") - self.SAbutton.clicked.connect(self.SAgovCB) - self.DAbutton = QtWidgets.QPushButton("DA") - self.DAbutton.clicked.connect(self.DAgovCB) - self.BLbutton = QtWidgets.QPushButton("BL") - self.BLbutton.clicked.connect(self.BLgovCB) - hBoxColParams1.addWidget(govLabel) - hBoxColParams1.addWidget(self.SEbutton) - hBoxColParams1.addWidget(self.SAbutton) - hBoxColParams1.addWidget(self.DAbutton) - hBoxColParams1.addWidget(self.BLbutton) - govLabel2 = QtWidgets.QLabel("Current Governor State:") - self.governorMessage = QtEpicsPVLabel( - daq_utils.pvLookupDict["governorMessage"], - self, - 140, - highlight_on_change=False, - ) - hBoxColParams2.addWidget(govLabel2) - hBoxColParams2.addWidget(self.governorMessage.getEntry()) - - self.openShutterButton = QtWidgets.QPushButton("Open Photon Shutter") - self.openShutterButton.clicked.connect(self.parent.openPhotonShutterCB) - hBoxColParams25.addWidget(self.openShutterButton) - robotGB = QtWidgets.QGroupBox() - robotGB.setTitle("Robot") - - self.unmountColdButton = QtWidgets.QPushButton("Unmount Cold") - self.unmountColdButton.clicked.connect(self.unmountColdCB) - self.testRobotButton = QtWidgets.QPushButton("Test Robot") - self.testRobotButton.clicked.connect(self.testRobotCB) - self.recoverRobotButton = QtWidgets.QPushButton("Recover Robot") - self.recoverRobotButton.clicked.connect(self.recoverRobotCB) - self.dryGripperButton = QtWidgets.QPushButton("Dry Gripper") - self.dryGripperButton.clicked.connect(self.dryGripperCB) - - hBoxColParams3.addWidget(self.unmountColdButton) - hBoxColParams3.addWidget(self.testRobotButton) - hBoxColParams3.addWidget(self.recoverRobotButton) - hBoxColParams3.addWidget(self.dryGripperButton) - robotGB.setLayout(hBoxColParams3) - - zebraGB = QtWidgets.QGroupBox() - detGB = QtWidgets.QGroupBox() - zebraGB.setTitle("Zebra (Timing)") - detGB.setTitle("Eiger Detector") - hBoxDet1 = QtWidgets.QHBoxLayout() - hBoxDet2 = QtWidgets.QHBoxLayout() - vBoxDet1 = QtWidgets.QVBoxLayout() - self.stopDetButton = QtWidgets.QPushButton("Stop") - self.stopDetButton.clicked.connect(self.stopDetCB) - self.rebootDetIocButton = QtWidgets.QPushButton("Reboot Det IOC") - self.rebootDetIocButton.clicked.connect(self.rebootDetIocCB) - detStatLabel = QtWidgets.QLabel("Detector Status:") - self.detMessage_ledit = QtWidgets.QLabel() - hBoxDet1.addWidget(self.stopDetButton) - hBoxDet1.addWidget(self.rebootDetIocButton) - hBoxDet2.addWidget(detStatLabel) - hBoxDet2.addWidget(self.detMessage_ledit) - - beamGB = QtWidgets.QGroupBox() - beamGB.setTitle("Beam") - hBoxBeam1 = QtWidgets.QHBoxLayout() - hBoxBeam2 = QtWidgets.QHBoxLayout() - hBoxBeam3 = QtWidgets.QHBoxLayout() - vBoxBeam = QtWidgets.QVBoxLayout() - if daq_utils.beamline == "fmx": - slit1XLabel = QtWidgets.QLabel("Slit 1 X Gap:") - slit1XLabel.setAlignment(QtCore.Qt.AlignCenter) - slit1XRBLabel = QtWidgets.QLabel("Readback:") - self.slit1XRBVLabel = QtEpicsPVLabel( - daq_utils.motor_dict["slit1XGap"] + ".RBV", self, 70 - ) - slit1XSPLabel = QtWidgets.QLabel("SetPoint:") - self.slit1XMotor_ledit = QtWidgets.QLineEdit() - self.slit1XMotor_ledit.returnPressed.connect(self.setSlit1XCB) - self.slit1XMotor_ledit.setText(str(self.parent.slit1XGapSP_pv.get())) - - slit1YLabel = QtWidgets.QLabel("Slit 1 Y Gap:") - slit1YLabel.setAlignment(QtCore.Qt.AlignCenter) - slit1YRBLabel = QtWidgets.QLabel("Readback:") - self.slit1YRBVLabel = QtEpicsPVLabel( - daq_utils.motor_dict["slit1YGap"] + ".RBV", self, 70 - ) - slit1YSPLabel = QtWidgets.QLabel("SetPoint:") - self.slit1YMotor_ledit = QtWidgets.QLineEdit() - self.slit1YMotor_ledit.setText(str(self.parent.slit1YGapSP_pv.get())) - self.slit1YMotor_ledit.returnPressed.connect(self.setSlit1YCB) - - sampleFluxLabelDesc = QtWidgets.QLabel("Sample Flux:") - sampleFluxLabelDesc.setFixedWidth(80) - self.sampleFluxLabel = QtWidgets.QLabel() - self.sampleFluxLabel.setText("%E" % self.parent.sampleFluxPV.get()) - hBoxBeam3.addWidget(sampleFluxLabelDesc) - hBoxBeam3.addWidget(self.sampleFluxLabel) - - if daq_utils.beamline == "fmx": - hBoxBeam1.addWidget(slit1XLabel) - hBoxBeam1.addWidget(slit1XRBLabel) - hBoxBeam1.addWidget(self.slit1XRBVLabel.getEntry()) - hBoxBeam1.addWidget(slit1XSPLabel) - hBoxBeam1.addWidget(self.slit1XMotor_ledit) - hBoxBeam2.addWidget(slit1YLabel) - hBoxBeam2.addWidget(slit1YRBLabel) - hBoxBeam2.addWidget(self.slit1YRBVLabel.getEntry()) - hBoxBeam2.addWidget(slit1YSPLabel) - hBoxBeam2.addWidget(self.slit1YMotor_ledit) - vBoxBeam.addLayout(hBoxBeam1) - vBoxBeam.addLayout(hBoxBeam2) - vBoxBeam.addLayout(hBoxBeam3) - beamGB.setLayout(vBoxBeam) - - vBoxDet1.addLayout(hBoxDet1) - vBoxDet1.addLayout(hBoxDet2) - detGB.setLayout(vBoxDet1) - hBoxColParams4 = QtWidgets.QHBoxLayout() - vBoxZebraParams4 = QtWidgets.QVBoxLayout() - self.resetZebraButton = QtWidgets.QPushButton("Reset Zebra") - self.resetZebraButton.clicked.connect(self.resetZebraCB) - self.rebootZebraButton = QtWidgets.QPushButton("Reboot Zebra IOC") - self.rebootZebraButton.clicked.connect(self.rebootZebraIOC_CB) - hBoxColParams5 = QtWidgets.QHBoxLayout() - self.zebraArmCheckBox = QCheckBox("Arm") - self.zebraArmCheckBox.setEnabled(False) - self.zebraPulseCheckBox = QCheckBox("Pulse") - self.zebraPulseCheckBox.setEnabled(False) - self.zebraDownloadCheckBox = QCheckBox("Downloading") - self.zebraDownloadCheckBox.setEnabled(False) - self.zebraSentTriggerCheckBox = QCheckBox("Trigger Sent") - self.zebraSentTriggerCheckBox.setEnabled(False) - self.zebraReturnedTriggerCheckBox = QCheckBox("Trigger Returned") - self.zebraReturnedTriggerCheckBox.setEnabled(False) - hBoxColParams4.addWidget(self.resetZebraButton) - hBoxColParams4.addWidget(self.rebootZebraButton) - hBoxColParams5.addWidget(self.zebraArmCheckBox) - hBoxColParams5.addWidget(self.zebraPulseCheckBox) - hBoxColParams5.addWidget(self.zebraDownloadCheckBox) - hBoxColParams5.addWidget(self.zebraSentTriggerCheckBox) - hBoxColParams5.addWidget(self.zebraReturnedTriggerCheckBox) - vBoxZebraParams4.addLayout(hBoxColParams4) - vBoxZebraParams4.addLayout(hBoxColParams5) - zebraGB.setLayout(vBoxZebraParams4) - - self.buttons = QtWidgets.QDialogButtonBox( - QtWidgets.QDialogButtonBox.Ok, QtCore.Qt.Horizontal, self - ) - self.buttons.buttons()[0].clicked.connect(self.userScreenOKCB) - - if daq_utils.beamline == "nyx": - self.openShutterButton.setDisabled(True) - self.unmountColdButton.setDisabled(True) - self.testRobotButton.setDisabled(True) - self.recoverRobotButton.setDisabled(True) - self.dryGripperButton.setDisabled(True) - self.resetZebraButton.setDisabled(True) - self.rebootZebraButton.setDisabled(True) - self.stopDetButton.setDisabled(True) - self.rebootDetIocButton.setDisabled(True) - - vBoxColParams1.addLayout(hBoxColParams1) - vBoxColParams1.addLayout(hBoxColParams2) - vBoxColParams1.addLayout(hBoxColParams25) - vBoxColParams1.addWidget(robotGB) - vBoxColParams1.addWidget(zebraGB) - vBoxColParams1.addWidget(detGB) - vBoxColParams1.addWidget(beamGB) - - vBoxColParams1.addWidget(self.buttons) - self.setLayout(vBoxColParams1) - - def setSlit1XCB(self): - comm_s = "setSlit1X(" + str(self.slit1XMotor_ledit.text()) + ")" - self.parent.send_to_server(comm_s) - - def setSlit1YCB(self): - comm_s = "setSlit1Y(" + str(self.slit1YMotor_ledit.text()) + ")" - self.parent.send_to_server(comm_s) - - def unmountColdCB(self): - self.parent.send_to_server("unmountCold()") - - def testRobotCB(self): - self.parent.send_to_server("testRobot()") - - def recoverRobotCB(self): - self.parent.send_to_server("recoverRobot()") - - def dryGripperCB(self): - self.parent.send_to_server("dryGripper()") - - def stopDetCB(self): - logger.info("stopping detector") - self.parent.stopDet_pv.put(0) - - def rebootDetIocCB(self): - logger.info("rebooting detector IOC") - self.parent.rebootDetIOC_pv.put( - 1 - ) # no differences visible, but zebra IOC reboot works, this doesn't! - - def resetZebraCB(self): - logger.info("resetting zebra") - self.parent.resetZebra_pv.put(1) - - def rebootZebraIOC_CB(self): - logger.info("rebooting zebra IOC") - self.parent.rebootZebraIOC_pv.put(1) - - def SEgovCB(self): - self.parent.send_to_server("setGovRobot(gov_robot, 'SE')") - - def SAgovCB(self): - self.parent.send_to_server("setGovRobot(gov_robot, 'SA')") - - def DAgovCB(self): - self.parent.send_to_server("setGovRobot(gov_robot, 'DA')") - - def BLgovCB(self): - self.parent.send_to_server("setGovRobot(gov_robot, 'BL')") - - def userScreenOKCB(self): - self.hide() - - def screenDefaultsCancelCB(self): - self.done(QtWidgets.QDialog.Rejected) - - def screenDefaultsOKCB(self): - self.done(QtWidgets.QDialog.Accepted) diff --git a/gui/raster.py b/gui/raster.py deleted file mode 100644 index 49be3ff1..00000000 --- a/gui/raster.py +++ /dev/null @@ -1,98 +0,0 @@ -import logging -import typing - -from qtpy import QtCore, QtWidgets - -import albulaUtils - -if typing.TYPE_CHECKING: - from lsdcGui import ControlMain - -logger = logging.getLogger() - - -class RasterCell(QtWidgets.QGraphicsRectItem): - def __init__(self, x, y, w, h, topParent): - super(RasterCell, self).__init__(x, y, w, h, None) - self.topParent = topParent - - -def isInCell(position, item): - if item.contains(position): - return True - return False - - -class RasterGroup(QtWidgets.QGraphicsItemGroup): - def __init__(self, parent: "ControlMain"): - super(RasterGroup, self).__init__() - self.parent = parent - self.setAcceptHoverEvents(True) - self.currentSelectedCell = None - - def mousePressEvent(self, e): - for i in range(len(self.parent.rasterList)): - if self.parent.rasterList[i] != None: - if self.parent.rasterList[i]["graphicsItem"].isSelected(): - logger.info("found selected raster") - self.parent.SelectedItemData = self.parent.rasterList[i]["uid"] - self.parent.treeChanged_pv.put(1) - if self.parent.vidActionRasterExploreRadio.isChecked(): - for cell in self.childItems(): - if cell.contains(e.pos()): - if cell.data(0) != None: - if self.currentSelectedCell: - self.currentSelectedCell.setPen(self.parent.redPen) - self.currentSelectedCell.setZValue(0) - spotcount = cell.data(0) - filename = cell.data(1) - d_min = cell.data(2) - intensity = cell.data(3) - if self.parent.staffScreenDialog.albulaDispCheckBox.isChecked(): - if filename != "empty": - logger.debug( - f"filename to display: {filename} spotcount: {spotcount} dmin: {d_min} intensity: {intensity}" - ) - albulaUtils.albulaDispFile(filename) - if not (self.parent.rasterExploreDialog.isVisible()): - self.parent.rasterExploreDialog.show() - self.parent.rasterExploreDialog.setSpotCount(spotcount) - self.parent.rasterExploreDialog.setTotalIntensity(intensity) - self.parent.rasterExploreDialog.setResolution(d_min) - groupList = self.childItems() - cell.setPen(self.parent.yellowPen) - cell.setZValue(1) - self.currentSelectedCell = cell - break - else: - super(RasterGroup, self).mousePressEvent(e) - - def mouseMoveEvent(self, e): - if e.buttons() == QtCore.Qt.LeftButton: - pass - if e.buttons() == QtCore.Qt.RightButton: - pass - - super(RasterGroup, self).mouseMoveEvent(e) - logger.debug(f"pos:{self.pos()} event:{e}") # TODO: Add event description - - def mouseReleaseEvent(self, e): - super(RasterGroup, self).mouseReleaseEvent(e) - if e.button() == QtCore.Qt.LeftButton: - pass - if e.button() == QtCore.Qt.RightButton: - pass - - def hoverMoveEvent(self, e): - super(RasterGroup, self).hoverEnterEvent(e) - for cell in self.childItems(): - if isInCell(e.scenePos(), cell): - if cell.data(0) != None: - spotcount = cell.data(0) - d_min = cell.data(2) - intensity = cell.data(3) - if not (self.parent.rasterExploreDialog.isVisible()): - self.parent.rasterExploreDialog.show() - self.parent.rasterExploreDialog.setSpotCount(spotcount) - self.parent.rasterExploreDialog.setTotalIntensity(intensity) - self.parent.rasterExploreDialog.setResolution(d_min) diff --git a/ispybLib.py b/ispybLib.py index 4c78f2a5..bc0fd2ed 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -8,24 +8,25 @@ import db_lib import det_lib import time +import mysql.connector import logging logger = logging.getLogger(__name__) #12/19 - I'm leaving all commented lines alone on this. Karl Levik, DLS, is an immense help with this. -conf_file = "/etc/ispyb/ispybConfig.cfg" +conf_file = os.environ["CONFIGDIR"] + "ispybConfig.cfg" visit = 'mx99999-1' # Get a list of request dicts #request_dicts = lsdb2.getColRequestsByTimeInterval('2018-02-14T00:00:00','2018-02-15T00:00:00') # Connect to ISPyB, get the relevant data area objects -#conn = ispyb.open(conf_file) -#core = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.CORE, conn) -#mxacquisition = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXACQUISITION, conn) -#mxprocessing = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXPROCESSING, conn) -#mxscreening = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXSCREENING, conn) -#cnx = mysql.connector.connect(user='ispyb_api', password=os.environ['ISPYB_PASSWORD'],host='ispyb-db-dev.cs.nsls2.local',database='ispyb') -#cursor = cnx.cursor() +conn = ispyb.open(credentials=conf_file) +core = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.CORE, conn) +mxacquisition = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXACQUISITION, conn) +mxprocessing = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXPROCESSING, conn) +mxscreening = ispyb.factory.create_data_area(ispyb.factory.DataAreaType.MXSCREENING, conn) +cnx = mysql.connector.connect(user='ispyb_api', password=os.environ['ISPYB_PASSWORD'],host='ispyb-db.nsls2.bnl.local',database='ispyb') +cursor = cnx.cursor() beamline = os.environ["BEAMLINE_ID"] # Find the id for a particular @@ -393,7 +394,6 @@ def insertRasterResult(request,visitName): except ISPyBNoResultException as e: logger.error("insertRasterResult - caught ISPyBNoResultException: '%s'. make sure visit name is in the format mx999999-1234. NOT HAVING MX IN FRONT IS A SIGN OF PROBLEMS - try newVisit() in that case." % e) return - request = db_lib.getRequestByID(request_id) sample = request['sample'] # this needs to be created and linked to a DC group #result_obj = result['result_obj'] this doesn't appear to be used -DK request_obj = request['request_obj'] diff --git a/md2_flyers.py b/md2_flyers.py deleted file mode 100644 index b18251b6..00000000 --- a/md2_flyers.py +++ /dev/null @@ -1,269 +0,0 @@ -import logging -import os -from collections import deque -import getpass -import grp -from ophyd.sim import NullStatus -from ophyd.status import SubscriptionStatus - -logger = logging.getLogger(__name__) - -DEFAULT_DATUM_DICT = {"data": None, "omega": None} - -INTERNAL_SERIES = 0 -INTERNAL_ENABLE = 1 -EXTERNAL_SERIES = 2 -EXTERNAL_ENABLE = 3 - -class MD2StandardFlyer(): - def __init__(self, md2, detector=None) -> None: - self.name = "MD2StandardFlyer" - self.detector = detector - self.md2 = md2 - self.collection_params = {} - - self._asset_docs_cache = deque() - self._resource_uids = [] - self._datum_counter = None - self._datum_ids = DEFAULT_DATUM_DICT - self._master_file = None - self._master_metadata = [] - - self._collection_dictionary = None - - def kickoff(self): - md2_msg = self.md2.standard_scan(num_images=self.collection_params["total_num_images"], - start_angle=self.collection_params["start_angle"], - scan_range=self.collection_params["scan_range"], - exposure_time=self.collection_params["exposure_time"]) - logger.info(f"md2_msg: {md2_msg}") - return NullStatus() - - def update_parameters(self, total_num_images, start_angle, scan_range, exposure_time): - self.collection_params = { - "total_num_images": total_num_images, - "start_angle": start_angle, - "scan_range": scan_range, - "exposure_time": exposure_time, - } - - def configure_detector(self, file_prefix, data_directory_name): - self.detector.file.external_name.put(file_prefix) - self.detector.file.write_path_template = data_directory_name - - def detector_arm(self, angle_start, img_width, total_num_images, exposure_per_image, - file_prefix, data_directory_name, file_number_start, x_beam, y_beam, - wavelength, det_distance_m): - self.detector.cam.save_files.put(1) - self.detector.cam.sequence_id.put(file_number_start) - self.detector.cam.det_distance.put(det_distance_m) - self.detector.cam.file_owner.put(getpass.getuser()) - self.detector.cam.file_owner_grp.put(grp.getgrgid(os.getgid())[0]) - self.detector.cam.file_perms.put(420) - file_prefix_minus_directory = str(file_prefix) - file_prefix_minus_directory = file_prefix_minus_directory.split("/")[-1] - self.detector.cam.acquire_time.put(exposure_per_image) - self.detector.cam.acquire_period.put(exposure_per_image) - self.detector.cam.num_triggers.put(1) - self.detector.cam.num_images.put(total_num_images) - self.detector.cam.trigger_mode.put( - EXTERNAL_SERIES - ) # must be external_enable to get the correct number of triggers and stop acquire - self.detector.cam.file_path.put(data_directory_name) - self.detector.cam.fw_name_pattern.put(f"{file_prefix_minus_directory}_$id") - self.detector.cam.beam_center_x.put(x_beam) - self.detector.cam.beam_center_y.put(y_beam) - self.detector.cam.omega_incr.put(img_width) - self.detector.cam.omega_start.put(angle_start) - self.detector.cam.wavelength.put(wavelength) - self.detector.file.file_write_images_per_file.put(500) - - #def armed_callback(value, old_value, **kwargs): - # if old_value == 0 and value == 1: - # return True - # return False - - #status = SubscriptionStatus(self.detector.cam.armed, armed_callback, run=False) - #self.detector.cam.acquire.put(1) - #yield status - - def complete(self): - # monitor md2 status, wait for ready or timeout and then return - ready_status = self.md2.ready_status() - timeout = self.collection_params["exposure_time"] + 10 - ready_status.wait(timeout=timeout) - return ready_status - - def describe_collect(self): - return {"stream_name": {}} - #return {self.name: self._collection_dictionary} - - def collect(self): - logger.debug("raster_flyer.collect(): going to unstage now") - yield {"data": {}, "timestamps": {}, "time": 0, "seq_num": 0} - #return self._collection_dictionary - - def unstage(self): - logger.debug("flyer unstaging") - self.collection_params = {} - - def read_configuration(self): - return {} - - def describe_configuration(self): - return {} - - # def collect_asset_docs(self): - # for _ in (): - # yield _ - - def collect_asset_docs(self): - asset_docs_cache = [] - - # Get the Resource which was produced when the detector was staged. - ((name, resource),) = self.detector.file.collect_asset_docs() - - asset_docs_cache.append(("resource", resource)) - self._datum_ids = DEFAULT_DATUM_DICT - # Generate Datum documents from scratch here, because the detector was - # triggered externally by the DeltaTau, never by ophyd. - resource_uid = resource["uid"] - # num_points = int(math.ceil(self.detector.cam.num_images.get() / - # self.detector.cam.fw_num_images_per_file.get())) - - # We are currently generating only one datum document for all frames, that's why - # we use the 0th index below. - # - # Uncomment & update the line below if more datum documents are needed: - # for i in range(num_points): - - seq_id = self.detector.cam.sequence_id.get() - - self._master_file = f"{resource['root']}/{resource['resource_path']}_{seq_id}_master.h5" - if not os.path.isfile(self._master_file): - raise RuntimeError(f"File {self._master_file} does not exist") - - # The pseudocode below is from Tom Caswell explaining the relationship between resource, datum, and events. - # - # resource = { - # "resource_id": "RES", - # "resource_kwargs": {}, # this goes to __init__ - # "spec": "AD-EIGER-MX", - # ...: ..., - # } - # datum = { - # "datum_id": "a", - # "datum_kwargs": {"data_key": "data"}, # this goes to __call__ - # "resource": "RES", - # ...: ..., - # } - # datum = { - # "datum_id": "b", - # "datum_kwargs": {"data_key": "omega"}, - # "resource": "RES", - # ...: ..., - # } - - # event = {...: ..., "data": {"detector_img": "a", "omega": "b"}} - - for data_key in self._datum_ids.keys(): - datum_id = f"{resource_uid}/{data_key}" - self._datum_ids[data_key] = datum_id - datum = { - "resource": resource_uid, - "datum_id": datum_id, - "datum_kwargs": {"data_key": data_key}, - } - asset_docs_cache.append(("datum", datum)) - return tuple(asset_docs_cache) - - def _extract_metadata(self, field="omega"): - with h5py.File(self._master_file, "r") as hf: - return hf.get(f"entry/sample/goniometer/{field}")[()] - -class MD2VectorFlyer(MD2StandardFlyer): - def __init__(self, md2, detector=None) -> None: - super().__init__(md2, detector) - self.name = "MD2VectorFlyer" - - def kickoff(self): - # params used are start_angle, scan_range, exposure_time, start_y, start_z, stop_y, stop_z - md2_msg = self.md2.vector_scan(start_angle=self.collection_params["start_angle"], - scan_range=self.collection_params["scan_range"], - exposure_time=self.collection_params["exposure_time"], - start_cx=self.collection_params["start_cx"], - start_cy=self.collection_params["start_cy"], - start_y=self.collection_params["start_y"], - start_z=self.collection_params["start_z"], - stop_cx=self.collection_params["stop_cx"], - stop_cy=self.collection_params["stop_cy"], - stop_y=self.collection_params["stop_y"], - stop_z=self.collection_params["stop_z"],) - logger.info(f"md2_msg: {md2_msg}") - return NullStatus() - - def update_parameters(self, start_angle, scan_range, exposure_time, start_y, start_z, stop_y, stop_z, start_cx, start_cy, stop_cx, stop_cy): - self.collection_params = { - "start_angle": start_angle, - "scan_range": scan_range, - "exposure_time": exposure_time, - "start_cx": start_cx, - "start_cy": start_cy, - "start_y": start_y, - "start_z": start_z, - "stop_cx": stop_cx, - "stop_cy": stop_cy, - "stop_y": stop_y, - "stop_z": stop_z, - } - -class MD2RasterFlyer(MD2StandardFlyer): - # List of scan parameters values, "/t" separated. The axes start values define the beginning - # of the exposure, that is when all the axes have a steady speed and when the shutter/detector - # are triggered. - # The axes stop values are for the end of detector exposure and define the position at the - # beginning of the deceleration. - # Inputs names: "omega_range", "line_range", "total_uturn_range", "start_omega", "start_y", - # "start_z", "start_cx", "start_cy", "number_of_lines", "frames_per_lines", "exposure_time", - # "invert_direction", "use_centring_table", "use_fast_mesh_scans" - - def __init__(self, md2, detector=None) -> None: - super().__init__(md2, detector) - self.name = "MD2RasterFlyer" - - def kickoff(self): - # params used are start_angle, scan_range, exposure_time, start_y, start_z, stop_y, stop_z - md2_msg = self.md2.raster_scan(omega_range=self.collection_params["omega_range"], - line_range=self.collection_params["line_range"], - total_uturn_range=self.collection_params["total_uturn_range"], - start_omega=self.collection_params["start_omega"], - start_y=self.collection_params["start_y"], - start_z=self.collection_params["start_z"], - start_cx=self.collection_params["start_cx"], - start_cy=self.collection_params["start_cy"], - number_of_lines=self.collection_params["number_of_lines"], - frames_per_line=self.collection_params["frames_per_line"], - exposure_time=self.collection_params["exposure_time"], - invert_direction=self.collection_params["invert_direction"], - use_centring_table=self.collection_params["use_centring_table"], - use_fast_mesh_scans=self.collection_params["use_fast_mesh_scans"]) - logger.info(f"md2_msg: {md2_msg}") - return NullStatus() - - def update_parameters(self, omega_range, line_range, total_uturn_range, start_omega, start_y, start_z, start_cx, start_cy, number_of_lines, frames_per_line, exposure_time, invert_direction, use_centring_table, use_fast_mesh_scans): - self.collection_params = { - "omega_range": omega_range, - "line_range": line_range, - "total_uturn_range": total_uturn_range, - "start_omega": start_omega, - "start_y": start_y, - "start_z": start_z, - "start_cx": start_cx, - "start_cy": start_cy, - "number_of_lines": number_of_lines, - "frames_per_line": frames_per_line, - "exposure_time": exposure_time, - "invert_direction": invert_direction, - "use_centring_table": use_centring_table, - "use_fast_mesh_scans": use_fast_mesh_scans, - } diff --git a/raddoseLib.py b/raddoseLib.py index 4c1ef5a4..841fe033 100755 --- a/raddoseLib.py +++ b/raddoseLib.py @@ -8,80 +8,46 @@ import os.path import os import logging - logger = logging.getLogger(__name__) -# 12/19 - See Martin about this code! - +#12/19 - See Martin about this code! -def replaceLines(filename, replacementStrings): - lines = [] - with open(filename, "r") as f: - for line in f.readlines(): - for text_to_search, replacement_text in replacementStrings.items(): - if text_to_search in line: - line = replacement_text - break - lines.append(line) - - with open(filename, "w") as f: - f.writelines(lines) +def replaceLine(file,searchExp,replaceExp): + for line in fileinput.input(file, inplace=1): + if searchExp in line: + line = replaceExp + sys.stdout.write(line) def run_rd3d(inputFileName): - prc = subprocess.Popen( - [ - "java", - "-jar", - os.environ["CONFIGDIR"] + "/raddose3d.jar", - "-i", - inputFileName, - "-p", - f"rd3d/rd3d_{os.getpid()}_", - ], + prc = subprocess.Popen(["java", "-jar", os.environ['CONFIGDIR'] + "/raddose3d.jar", "-i", inputFileName, "-p", "rd3d/rd3d_"], stdout=subprocess.PIPE, - universal_newlines=True, - ) + universal_newlines=True) out = prc.communicate()[0] return out - -def rd3d_calc( - flux=3.5e12, - energy=12.66, - beamType="GAUSSIAN", - fwhmX=2, - fwhmY=1, - collimationX=10, - collimationY=10, - wedge=0, - exposureTime=1, - translatePerDegX=0, - translatePerDegY=0, - translatePerDegZ=0, - startOffsetX=0, - startOffsetY=0, - startOffsetZ=0, - dimX=20, - dimY=20, - dimZ=20, - pixelsPerMicron=2, - angularResolution=2, - templateFileName=os.environ["CONFIGDIR"] + "/rd3d_input_template.txt", - verbose=True, -): +def rd3d_calc(flux=3.5e12, energy=12.66, + beamType='GAUSSIAN', fwhmX=2, fwhmY=1, collimationX=10, collimationY=10, + wedge=0, exposureTime=1, + translatePerDegX=0, translatePerDegY=0, translatePerDegZ=0, + startOffsetX=0, startOffsetY=0, startOffsetZ=0, + dimX=20, dimY=20, dimZ=20, + pixelsPerMicron=2, angularResolution=2, + templateFileName = os.environ["CONFIGDIR"] + '/rd3d_input_template.txt', + verbose=True, + ): """ RADDOSE3D dose estimate - + This version calculates dose values for an average protein crystal. The estimates need to be adjusted proportionally for a crystal if it is more/less sensitive. - + All paramaters listed below can be set. If they are not set explicitly, RADDOSE3D will use the listed default value. - + A complete manual with explanations is available at https://github.com/GarmanGroup/RADDOSE-3D/blob/master/doc/user-guide.pdf - + Photon flux [ph/s]: flux=3.5e12 Photon energy [keV]: energy=12.66, Beamtype (GAUSSIAN | TOPHAT): beamType='GAUSSIAN' @@ -103,24 +69,24 @@ def rd3d_calc( Pixels per micron: pixelsPerMicron=2 Angular resolution: angularResolution=2 Template file (in 'rd3d' subdir of active notebook): templateFileName = 'rd3d_input_template.txt' - + Return value is a structured numpy array. You can use it for follow-up calculations of the results returned by RADDOSE3D in "output-Summary.csv". Call the return variable to find the field names. - + Examples: rd3d_out = rd3d_calc(flux=1.35e12, exposuretime=0.01, dimx=1, dimy=1, dimz=1) rd3d_calc(flux=1e12, energy=12.7, fwhmX=3, fwhmY=5, collimationX=9, collimationY=15, wedge=180, exposureTime=8, translatePerDegX=0, translatePerDegY=0.27, startOffsetY=-25, dimX=3, dimY=80, dimZ=3, pixelsPerMicron=0.5, angularResolution=2, verbose=False) - + Setup: * rd3d_input_template.txt and raddose.jar in subdir rd3d/ * PDB file 2vb1.pdb in notebook dir 2vb1.pdb rd3d/raddose.jar rd3d/rd3d_input_template.txt - + Todo: * Cannot call PDB file in subdir or active dir, i.e. only 'PDB 2vb1.pdb' works in rd3d_input_template.txt * run_rd3d() with subdir option @@ -130,217 +96,212 @@ def rd3d_calc( * Keep template file as option? Then it should be in same dir as PDB file (notebook dir) * Protein option PDB (on xf17id1 cannot reach PDB URL) or JJDUMMY (how to cite?) """ - + rd3d_dir = "rd3d" - inputFileName = f"rd3d_{os.getpid()}_input.txt" - outputFileName = f"rd3d_{os.getpid()}_Summary.csv" - - templateFilePath = os.path.join(rd3d_dir, templateFileName) - inputFilePath = os.path.join(rd3d_dir, inputFileName) - outputFilePath = os.path.join(rd3d_dir, outputFileName) + inputFileName = "rd3d_input.txt" + outputFileName = "rd3d_Summary.csv" + templateFilePath=os.path.join(rd3d_dir,templateFileName) + inputFilePath=os.path.join(rd3d_dir,inputFileName) + outputFilePath=os.path.join(rd3d_dir,outputFileName) + copyfile(templateFilePath, inputFilePath) - - replacementStrings = { - "FLUX": f"FLUX {flux:.2e}\n", - "ENERGY": f"ENERGY {energy:.2f}\n", - "TYPE GAUSSIAN": f"TYPE {beamType}\n", - "FWHM": f"FWHM {fwhmX:.1f} {fwhmY:.1f}\n", - "COLLIMATION": f"COLLIMATION RECTANGULAR {collimationX:.1f} {collimationY:.1f}\n", - "WEDGE": f"WEDGE 0 {wedge:0.1f}\n", - "EXPOSURETIME": f"EXPOSURETIME {exposureTime:0.3f}\n", - "TRANSLATEPERDEGREE": f"TRANSLATEPERDEGREE {translatePerDegX:0.4f} {translatePerDegY:0.4f} {translatePerDegZ:0.4f}\n", - "DIMENSION": f"DIMENSION {dimX:0.1f} {dimY:0.1f} {dimZ:0.1f}\n", - "PIXELSPERMICRON": f"PIXELSPERMICRON {pixelsPerMicron:0.1f}\n", - "ANGULARRESOLUTION": f"ANGULARRESOLUTION {angularResolution:0.1f}\n", - "STARTOFFSET": f"STARTOFFSET {startOffsetX} {startOffsetY} {startOffsetZ}\n", - } - - replaceLines(inputFilePath, replacementStrings) - + + replaceLine(inputFilePath,"FLUX",'FLUX {:.2e}\n'.format(flux)) + replaceLine(inputFilePath,"ENERGY",'ENERGY {:.2f}\n'.format(energy)) + replaceLine(inputFilePath,"TYPE GAUSSIAN",'TYPE {:s}\n'.format(beamType)) + replaceLine(inputFilePath,"FWHM",'FWHM {:.1f} {:.1f}\n'.format(fwhmX,fwhmY)) + replaceLine(inputFilePath,"COLLIMATION",'COLLIMATION RECTANGULAR {:.1f} {:.1f}\n'.format(collimationX,collimationY)) + replaceLine(inputFilePath,"WEDGE",'WEDGE 0 {:0.1f}\n'.format(wedge)) + replaceLine(inputFilePath,"EXPOSURETIME",'EXPOSURETIME {:0.3f}\n'.format(exposureTime)) + replaceLine(inputFilePath,"TRANSLATEPERDEGREE", + 'TRANSLATEPERDEGREE {:0.4f} {:0.4f} {:0.4f}\n'.format(translatePerDegX,translatePerDegY,translatePerDegZ)) + replaceLine(inputFilePath,"DIMENSION",'DIMENSION {:0.1f} {:0.1f} {:0.1f}\n'.format(dimX,dimY,dimZ)) + replaceLine(inputFilePath,"PIXELSPERMICRON",'PIXELSPERMICRON {:0.1f}\n'.format(pixelsPerMicron)) + replaceLine(inputFilePath,"ANGULARRESOLUTION",'ANGULARRESOLUTION {:0.1f}\n'.format(angularResolution)) + replaceLine(inputFilePath,"STARTOFFSET", + 'STARTOFFSET {:f} {:f} {:f}\n'.format(startOffsetX,startOffsetY,startOffsetZ)) + out = run_rd3d(inputFilePath) if verbose: logger.info(out) - - rd3d_out = np.genfromtxt(outputFilePath, delimiter=",", names=True) + + rd3d_out = np.genfromtxt(outputFilePath, delimiter=',', names=True) logger.info("\n=== rd3d_calc summary ===") # append_fields has issues with 1d arrays, use reshape() and [] to make len() work on size 1 array: # https://stackoverflow.com/questions/53137822/adding-a-field-to-a-structured-numpy-array-4 rd3d_out = rd3d_out.reshape(1) - logger.info("Diffraction weighted dose = " + "%.3f" % rd3d_out["DWD"] + " MGy") - logger.info("Max dose = " + "%.3f" % rd3d_out["Max_Dose"] + " MGy") - if rd3d_out["DWD"]: - t2gl = ( - exposureTime * 30 / rd3d_out["DWD"] - ) # Time to Garman limit based on diffraction weighted dose + logger.info("Diffraction weighted dose = " + "%.3f" % rd3d_out['DWD'] + " MGy") + logger.info("Max dose = " + "%.3f" % rd3d_out['Max_Dose'] + " MGy") + if rd3d_out['DWD']: + t2gl = exposureTime * 30 / rd3d_out['DWD'] # Time to Garman limit based on diffraction weighted dose else: t2gl = 0 - rd3d_out = rfn.append_fields(rd3d_out, "t2gl", [t2gl], usemask=False) - logger.info("Time to Garman limit = " + "%.3f" % rd3d_out["t2gl"] + " s") - + rd3d_out = rfn.append_fields(rd3d_out,'t2gl',[t2gl],usemask=False) + logger.info("Time to Garman limit = " + "%.3f" % rd3d_out['t2gl'] + " s") + return rd3d_out - # ## Experiment time to reach 10 MGy dose -# +# # ### Inputs: # * Flux: From flux-at-sample PV # * Beam size: For now, set by hand, or determine from PV, or get from get_beamsize(TBD) # * Crystal size: Match to beam size # * Vector length: Start with assumption, LSDC vector length is along X-axis. Update could use the real projections -# +# # * Exposure time = 1 s # * Translation per degree has to match total vector length -# +# # * RD3D output = AWD [MGy] -# +# # ### Input from LSDC: # * Protocol standard or vector # * Beamsize settings -# +# # ### Output: # * Experiment time [s] to Average Diffraction Weighted Dose = 10 MGy import epics - -def fmx_expTime( - avg_dwd=10, # Default of 10MGy - beamsizeV=1.0, - beamsizeH=2.0, - vectorL=0, - energy=12.66, - flux=-1, - wedge=180, - verbose=False, -): +def fmx_expTime(avg_dwd = 10, #Default of 10MGy + beamsizeV = 1.0, beamsizeH = 2.0, + vectorL = 0, + energy = 12.66, + flux = -1, + wedge = 180, + verbose = False + ): """ RD3D output = AWD [MGy] - - + + Parameters ---------- - + beamsizeV, beamsizeH: float Beam size (V, H) [um]. Default 1x2 (VxH). For now, set explicitly. - + vectorL: float Vector length [um]: Default 0 um. Make assumption that the vector is completely oriented along X-axis. - + energy: float Photon energy [keV]. Default 12.66 keV - + wedge: float Crystal rotation for complete experiment [deg]. Start at 0, end at wedge - + flux: float Flux at sample position [ph/s]. By default this value is copied from the beamline's flux-at-sample PV. Can also be set explicitly. - + verbose: boolean True: Print out RADDOSE3D output. Default False - - + + Internal parameters ------------------- - + Crystal size XYZ: Match to beam size perpendicular to (XZ), and to vector length along the rotation axis (Y) - - + + Returns ------- - + Experiment time [s] to Average Diffraction Weighted Dose = 10 MGy - - + + Todo ---- - + * Beamsize: Read from a beamsize PV, or get from a get_beamsize() function - Check CRL settings - Check BCU attenuator - If V1H1 then 10x10 (dep on E) - - If V0H0 then + - If V0H0 then - If BCU-Attn-T < 1.0 then 3x5 - If BCU-Attn-T = 1.0 then 1x2 * Vector length: Use the real projections """ - + # Beam size [um] fwhmX = beamsizeV fwhmY = beamsizeH - collimationX = 3 * beamsizeV - collimationY = 3 * beamsizeH - + collimationX = 3*beamsizeV + collimationY = 3*beamsizeH + # Set explicitly or use current flux if flux == -1: # Current flux [ph/s]: From flux-at-sample PV - fluxSample = epics.caget("XF:17IDA-OP:FMX{Mono:DCM-dflux-MA}") - logger.info("Flux at sample = {:.4g} ph/s".format(fluxSample)) + fluxSample = epics.caget('XF:17IDA-OP:FMX{Mono:DCM-dflux-MA}') + logger.info('Flux at sample = {:.4g} ph/s'.format(fluxSample)) else: - fluxSample = flux - + fluxSample = flux + # Crystal size [um]: Match to beam size in V, longer than vector in H # XYZ as defined by Raddose3D dimX = beamsizeV # Crystal dimension V [um] dimY = vectorL + beamsizeH # Crystal dimension H [um] dimZ = dimX # Crystal dimension along beam [um] - + # Start offset for horizontal vector to stay within crystal [um] startOffsetY = -vectorL / 2 - + # Exposure time [s] exposureTime = 1.0 - + # Avoid division by zero when calculating translatePerDegY - if wedge == 0: - wedge = 1e-3 - + if wedge == 0: wedge = 1e-3 + # Vector length [um]: Assume LSDC vector length is along X-axis (Raddose3D Y). # Translation per degree has to match total vector length translatePerDegY = vectorL / wedge try: - rd3d_out = rd3d_calc( - flux=fluxSample, - energy=energy, - fwhmX=fwhmX, - fwhmY=fwhmY, - collimationX=collimationX, - collimationY=collimationY, - wedge=wedge, - exposureTime=exposureTime, - translatePerDegY=translatePerDegY, - startOffsetY=startOffsetY, - pixelsPerMicron=5, - angularResolution=1, - dimX=dimX, - dimY=dimY, - dimZ=dimZ, - verbose=verbose, - ) - + rd3d_out = rd3d_calc(flux=fluxSample, energy=energy, + fwhmX=fwhmX, fwhmY=fwhmY, + collimationX=collimationX, collimationY=collimationY, + wedge=wedge, + exposureTime=exposureTime, + translatePerDegY=translatePerDegY, + startOffsetY=startOffsetY, + pixelsPerMicron=5, angularResolution=1, + dimX=dimX, dimY=dimY, dimZ=dimZ, + verbose=verbose + ) + logger.info("\n=== fmx_expTime summary ===") - dose1s = float(rd3d_out["DWD"].item()) # .item() to convert 1d array to scalar - logger.info( - "Average Diffraction Weighted Dose for 1s exposure = {:f} MGy".format( - dose1s - ) - ) + dose1s = rd3d_out['DWD'].item() # .item() to convert 1d array to scalar + logger.info('Average Diffraction Weighted Dose for 1s exposure = {:f} MGy'.format(dose1s)) except Exception as e: - logger.error(f"Exception in rd3d calc: {e}") - dose1s = 0.0 - + logger.error(f'Exception in rd3d calc: {e}') + dose1s = 0 + if dose1s > 0: - expTimeMGy = ( - avg_dwd / dose1s - ) # Experiment time to reach an average DWD (avg_dwd) + expTimeMGy = avg_dwd / dose1s # Experiment time to reach an average DWD (avg_dwd) else: - expTimeMGy = 0.0 - logger.info( - f"Experiment time to reach an average diffraction weighted dose of {avg_dwd} MGy = {expTimeMGy} s" - ) - + expTimeMGy = 0 + logger.info(f'Experiment time to reach an average diffraction weighted dose of {avg_dwd} MGy = {expTimeMGy} s') + return expTimeMGy + + + +# Copy of Wuxian's 100 um vector (http://www.raddo.se/rd3d/job.php?u=16843&s=mLl5kFm5oXgRgpnf&id=16915) +#rd3d_calc(flux=1e12, energy=12.7, +# fwhmX=3, fwhmY=5, collimationX=9, collimationY=15, +# wedge=180, +# exposureTime=16, +# translatePerDegX=0, translatePerDegY=0.556, +# startOffsetY=-50, +# dimX=3, dimY=110, dimZ=3, +# pixelsPerMicron=0.5, angularResolution=2, +# ) + + +#fmx_expTime_to_10MGy(beamsizeV = 3.0, beamsizeH = 5.0, vectorL = 50, energy = 12.7, wedge = 180, flux = 1e12) + + +#fmx_expTime_to_10MGy(beamsizeV = 3.0, beamsizeH = 5.0, vectorL = 100, energy = 12.7, wedge = 180, flux = 1e12, verbose = True) diff --git a/robot_lib.py b/robot_lib.py index 374478e3..a279de66 100644 --- a/robot_lib.py +++ b/robot_lib.py @@ -26,21 +26,21 @@ def mountRobotSample(gov_robot, puck_pos, pin_pos, samp_id, **kwargs): if status != MOUNT_STEP_SUCCESSFUL: return status status = robot.postMount(gov_robot, puck_pos, pin_pos, samp_id) - return status + return MOUNT_SUCCESSFUL # TODO hard-coded for testing def unmountRobotSample(gov_robot, puck_pos, pin_pos, samp_id): status = robot.preUnmount(gov_robot, puck_pos, pin_pos, samp_id) if status != UNMOUNT_STEP_SUCCESSFUL: return status - if robot.control_type() == "Bluesky": RE(robot.unmount(gov_robot, puck_pos, pin_pos, samp_id)) - + else: + robot.unmount(gov_robot, puck_pos, pin_pos, samp_id) if isinstance(robot, OphydRobot): status = robot.check_sample_mounted(mount=False, puck_pos=puck_pos, pin_pos=pin_pos) else: - status = robot.unmount(gov_robot, puck_pos, pin_pos, samp_id) - return status + status = UNMOUNT_STEP_SUCCESSFUL # TODO assume embl robot is successful + return UNMOUNT_SUCCESSFUL # TODO hard-coded for testing def finish(): diff --git a/setenergy_lsdc.py b/setenergy_lsdc.py deleted file mode 100644 index 21f68bd6..00000000 --- a/setenergy_lsdc.py +++ /dev/null @@ -1,1239 +0,0 @@ -from ophyd import (SingleTrigger, ProsilicaDetector, - ImagePlugin, TIFFPlugin, StatsPlugin, ROIPlugin, DetectorBase, HDF5Plugin, - TransformPlugin, ProcessPlugin, AreaDetector) - -from ophyd import Component as Cpt -from ophyd import Device -from ophyd import EpicsSignal, EpicsSignalRO -from ophyd import EpicsMotor -from ophyd import PVPositioner, PVPositionerPC -import bluesky.plan_stubs as bps -import bluesky.plans as bp -import bluesky.preprocessors as bpp -import epics -import numpy as np -import pandas as pd -import socket -import time -import gov_lib -from start_bs import db, gov_robot, govs -import logging - - -logger = logging.getLogger() -flux_df = None -# Machine ========================================================== - -beam_current = EpicsSignal('SR:OPS-BI{DCCT:1}I:Real-I') - -class InsertionDevice(Device): - gap = Cpt(EpicsMotor, '-Ax:Gap}-Mtr', - kind='hinted', name='') - brake = Cpt(EpicsSignal, '}BrakesDisengaged-Sts', - write_pv='}BrakesDisengaged-SP', - kind='omitted', add_prefix=('read_pv', 'write_pv', 'suffix')) - - def set(self, *args, **kwargs): - self.brake.set(1).wait(5) - return self.gap.set(*args, **kwargs) - - def stop(self, *, success=False): - return self.gap.stop(success=success) - -ivu_gap = InsertionDevice('SR:C17-ID:G1{IVU21:2', name='ivu') - -# Photon Local Feedback, sector 17 orbit angle correction onto FMX XBPM1 -class PhotonLocalFeedback(Device): - x_enable = Cpt(EpicsSignal, 'X-FdbkEnabled') - y_enable = Cpt(EpicsSignal, 'Y-FdbkEnabled') - -photon_local_feedback_c17 = PhotonLocalFeedback('SR:APHLA:LBAgent{BUMP:C17-R7X2}', name='photon_local_feedback') - - -# Motors ============================================================ - -class YMotor(Device): - y = Cpt(EpicsMotor, '-Ax:Y}Mtr', labels=['fmx']) - -class XYMotor(Device): - x = Cpt(EpicsMotor, '-Ax:X}Mtr', labels=['fmx']) - y = Cpt(EpicsMotor, '-Ax:Y}Mtr', labels=['fmx']) - -class XYZMotor(XYMotor): - z = Cpt(EpicsMotor, '-Ax:Z}Mtr', labels=['fmx']) - -### 2DO: XZXYMotor -> XZMotor -class XZXYMotor(Device): - x = Cpt(EpicsMotor, '-Ax:X}Mtr', labels=['fmx']) - z = Cpt(EpicsMotor, '-Ax:Z}Mtr', labels=['fmx']) - -class Slits(Device): - b = Cpt(EpicsMotor, '-Ax:B}Mtr', labels=['fmx']) - i = Cpt(EpicsMotor, '-Ax:I}Mtr', labels=['fmx']) - o = Cpt(EpicsMotor, '-Ax:O}Mtr', labels=['fmx']) - t = Cpt(EpicsMotor, '-Ax:T}Mtr', labels=['fmx']) - x_ctr = Cpt(EpicsMotor, '-Ax:XCtr}Mtr', labels=['fmx']) - x_gap = Cpt(EpicsMotor, '-Ax:XGap}Mtr', labels=['fmx']) - y_ctr = Cpt(EpicsMotor, '-Ax:YCtr}Mtr', labels=['fmx']) - y_gap = Cpt(EpicsMotor, '-Ax:YGap}Mtr', labels=['fmx']) - -class DCM(Device): - b = Cpt(EpicsMotor, '-Ax:B}Mtr', labels=['fmx']) - g = Cpt(EpicsMotor, '-Ax:G}Mtr', labels=['fmx']) - p = Cpt(EpicsMotor, '-Ax:P}Mtr', labels=['fmx']) - r = Cpt(EpicsMotor, '-Ax:R}Mtr', labels=['fmx']) - e = Cpt(EpicsMotor, '-Ax:E}Mtr', labels=['fmx']) -# w = Cpt(EpicsMotor, '-Ax:W}Mtr', labels=['fmx']) - -class XYPitchMotor(XYMotor): - pitch = Cpt(EpicsMotor, '-Ax:P}Mtr') - -class KBMirror(Device): - hp = Cpt(EpicsMotor, ':KBH-Ax:P}Mtr') - hr = Cpt(EpicsMotor, ':KBH-Ax:R}Mtr') - hx = Cpt(EpicsMotor, ':KBH-Ax:X}Mtr') - hy = Cpt(EpicsMotor, ':KBH-Ax:Y}Mtr') - vp = Cpt(EpicsMotor, ':KBV-Ax:P}Mtr') - vx = Cpt(EpicsMotor, ':KBV-Ax:X}Mtr') - vy = Cpt(EpicsMotor, ':KBV-Ax:Y}Mtr') - - -class Cover(Device): - close = Cpt(EpicsSignal, 'Cmd:Cls-Cmd') - open = Cpt(EpicsSignal, 'Cmd:Opn-Cmd') - status = Cpt(EpicsSignalRO, 'Pos-Sts') # status: 0 (Not Open), 1 (Open) - -class Shutter(Device): - close = Cpt(EpicsSignal, 'Cmd:Cls-Cmd.PROC') - open = Cpt(EpicsSignal, 'Cmd:Opn-Cmd.PROC') - status = Cpt(EpicsSignalRO, 'Pos-Sts') # status: 0 (Open), 1 (Closed), 2 (Undefined) - -class GoniometerStack(Device): - gx = Cpt(EpicsMotor, '-Ax:GX}Mtr', labels=['fmx']) - gy = Cpt(EpicsMotor, '-Ax:GY}Mtr', labels=['fmx']) - gz = Cpt(EpicsMotor, '-Ax:GZ}Mtr', labels=['fmx']) - o = Cpt(EpicsMotor, '-Ax:O}Mtr', labels=['fmx']) - py = Cpt(EpicsMotor, '-Ax:PY}Mtr', labels=['fmx']) - pz = Cpt(EpicsMotor, '-Ax:PZ}Mtr', labels=['fmx']) - -## Horizontal Double Crystal Monochromator (FMX) -hdcm = DCM('XF:17IDA-OP:FMX{Mono:DCM', name='hdcm') - -# Vertical Double Crystal Monochromator (AMX) -dcm_amx = DCM('XF:17IDA-OP:AMX{Mono:DCM', name='dcm_amx') - - -## Horizontal Focusing Mirror - XYPitchMotor -hfm = XYPitchMotor('XF:17IDA-OP:FMX{Mir:HFM', name='hfm') - -## KB Mirror -kbm = KBMirror('XF:17IDC-OP:FMX{Mir', name='kbm') - - -## 17-ID-A FOE shutter -shutter_foe = Shutter('XF:17ID-PPS:FAMX{Sh:FE}', name='shutter_foe', - read_attrs=['status']) - -## 17-ID-C experimental hutch shutter -shutter_hutch_c = Shutter('XF:17IDA-PPS:FMX{PSh}', name='shutter_hutch_c', - read_attrs=['status']) - -## FMX BCU shutter -shutter_bcu = Shutter('XF:17IDC-ES:FMX{Gon:1-Sht}', name='shutter_bcu', - read_attrs=['status']) - -## Eiger16M detector cover -cover_detector = Cover('XF:17IDC-ES:FMX{Det:FMX-Cover}', name='cover_detector', - read_attrs=['status']) - -## Slits Motions -slits1 = Slits('XF:17IDA-OP:FMX{Slt:1', name='slits1', labels=['fmx']) - -## Light -light = YMotor('XF:17IDC-ES:FMX{Light:1', name='lightY') - -## Temporary: Disable motor alarm until end switch is fixed -from ophyd.utils.epics_pvs import AlarmSeverity -light.y.tolerated_alarm = AlarmSeverity.MAJOR - -## Goniometer Stack -gonio = GoniometerStack('XF:17IDC-ES:FMX{Gon:1', name='gonio') - -# Detectors =================================================================================== - -keithley = EpicsSignalRO('XF:17IDC-BI:FMX{Keith:1}readFloat', name='keithley') - -class StandardProsilica(SingleTrigger, ProsilicaDetector): - image = Cpt(ImagePlugin, 'image1:') - roi1 = Cpt(ROIPlugin, 'ROI1:') - roi2 = Cpt(ROIPlugin, 'ROI2:') - roi3 = Cpt(ROIPlugin, 'ROI3:') - roi4 = Cpt(ROIPlugin, 'ROI4:') - trans1 = Cpt(TransformPlugin, 'Trans1:') - proc1 = Cpt(ProcessPlugin, 'Proc1:') - stats1 = Cpt(StatsPlugin, 'Stats1:') - stats2 = Cpt(StatsPlugin, 'Stats2:') - stats3 = Cpt(StatsPlugin, 'Stats3:') - stats4 = Cpt(StatsPlugin, 'Stats4:') - stats5 = Cpt(StatsPlugin, 'Stats5:') - tiff = Cpt(TIFFPlugin, 'TIFF1:') - -cam_7 = StandardProsilica('XF:17IDC-ES:FMX{Cam:7}', name='cam_7') -cam_8 = StandardProsilica('XF:17IDC-ES:FMX{Cam:8}', name='cam_8') - -all_standard_pros = [cam_7, cam_8] - -for camera in all_standard_pros: - camera.read_attrs = ['stats1', 'stats2', 'stats3', 'stats4', 'stats5'] - camera.stats1.read_attrs = ['total', 'centroid'] - camera.stats2.read_attrs = ['total', 'centroid'] - camera.stats3.read_attrs = ['total', 'centroid'] - camera.stats4.read_attrs = ['total', 'centroid', 'sigma_x', 'sigma_y'] - camera.stats5.read_attrs = ['total', 'centroid'] - camera.stats4.centroid.read_attrs = ['x', 'y'] - camera.tiff.read_attrs = [] - -# BPM ======================================================================================= - -class Bpm(Device): - x = Cpt(EpicsSignalRO, 'PosX:MeanValue_RBV') - y = Cpt(EpicsSignalRO, 'PosY:MeanValue_RBV') - a = Cpt(EpicsSignalRO, 'Current1:MeanValue_RBV') - b = Cpt(EpicsSignalRO, 'Current2:MeanValue_RBV') - c = Cpt(EpicsSignalRO, 'Current3:MeanValue_RBV') - d = Cpt(EpicsSignalRO, 'Current4:MeanValue_RBV') - sum_x = Cpt(EpicsSignalRO, 'SumX:MeanValue_RBV') - sum_y = Cpt(EpicsSignalRO, 'SumY:MeanValue_RBV') - sum_all = Cpt(EpicsSignalRO, 'SumAll:MeanValue_RBV') - -bpm1 = Bpm('XF:17IDA-BI:FMX{BPM:1}', name='bpm1') - -bpm1.sum_all.kind = 'hinted' - -bpm1_sum_all_precision = EpicsSignal('XF:17IDA-BI:FMX{BPM:1}SumAll:MeanValue_RBV.PREC') -bpm1_sum_all_precision.put(10) - -class Xbpm(Device): - x = Cpt(EpicsSignalRO, 'Pos:X-I') - y = Cpt(EpicsSignalRO, 'Pos:Y-I') - a = Cpt(EpicsSignalRO, 'Ampl:ACurrAvg-I') - b = Cpt(EpicsSignalRO, 'Ampl:BCurrAvg-I') - c = Cpt(EpicsSignalRO, 'Ampl:CCurrAvg-I') - d = Cpt(EpicsSignalRO, 'Ampl:DCurrAvg-I') - total = Cpt(EpicsSignalRO, 'Ampl:CurrTotal-I') - -xbpm2 = Xbpm('SR:C17-BI{XBPM:2}', name='xbpm2') - -# Attenuators, CRL ========================================================================= - -class Transmission(Device): - energy = Cpt(EpicsSignal, 'Energy-SP') # PV only used for debugging. Attenuator uses Bragg axis energy - transmission = Cpt(EpicsSignal, 'Trans-SP') - set_trans = Cpt(EpicsSignal, 'Cmd:Set-Cmd.PROC') - -## Dummy Attenuator - for read/write_lut() and XF:17ID-ES:FMX{Misc-LUT:atten}X-Wfm/Y-Wfm -class AttenuatorLUT(Device): - done = Cpt(EpicsSignalRO, '}attenDone') - -class AttenuatorBCU(Device): - a1 = Cpt(EpicsMotor, '-Ax:1}Mtr', labels=['fmx']) - a2 = Cpt(EpicsMotor, '-Ax:2}Mtr', labels=['fmx']) - a3 = Cpt(EpicsMotor, '-Ax:3}Mtr', labels=['fmx']) - a4 = Cpt(EpicsMotor, '-Ax:4}Mtr', labels=['fmx']) - done = Cpt(EpicsSignalRO, '}attenDone') - -## BCU Transmission -trans_bcu = Transmission('XF:17IDC-OP:FMX{Attn:BCU}', name='trans_bcu', - read_attrs=['transmission']) -## RI Transmission -trans_ri = Transmission('XF:17IDC-OP:FMX{Attn:RI}', name='trans_ri', - read_attrs=['transmission']) - -## Dummy Attenuator - for read/write_lut() and XF:17ID-ES:FMX{Misc-LUT:atten}X-Wfm/Y-Wfm -atten = AttenuatorLUT('XF:17IDC-OP:FMX{Attn:BCU', name='atten', - read_attrs=['done']) - -## BCU Attenuator -atten_bcu = AttenuatorBCU('XF:17IDC-OP:FMX{Attn:BCU', name='atten_bcu', - read_attrs=['done', 'a1', 'a2', 'a3', 'a4'], - labels=['fmx']) - -# KB mirror pitch tweak voltages -vkb_piezo_tweak = EpicsSignal('XF:17IDC-BI:FMX{Best:2}:PreDAC0:OutCh1') -hkb_piezo_tweak = EpicsSignal('XF:17IDC-BI:FMX{Best:2}:PreDAC0:OutCh2') - -# Utility ================================================================================= - -class BeamlineCalibrations(Device): - LoMagCal = Cpt(EpicsSignal, 'LoMagCal}') - HiMagCal = Cpt(EpicsSignal, 'HiMagCal}') - -BL_calibration = BeamlineCalibrations('XF:17ID-ES:FMX{Misc-', - name='BL_calibration', - read_attrs=['LoMagCal', 'HiMagCal']) - -def blStrGet(): - """ - Return beamline string - - blStr: 'AMX' or 'FMX' - - Beamline is determined by querying hostname - """ - hostStr = socket.gethostname() - if hostStr.startswith('xf17id2'): - blStr = 'FMX' - elif hostStr.startswith('xf17id1'): - blStr = 'AMX' - else: - print('Error - this code must be executed on one of the -ca1 machines') - blStr = -1 - - return blStr - - -# Plans to set beamline energy ======================================================================= - -## Helper functions for set_energy and alignment - -def find_peak(det, mot, start, stop, steps): - print(f"Scanning {mot.name} vs {det.name}...") - - uid = yield from bp.relative_scan([det], mot, start, stop, steps) - - sp = '_gap_user_setpoint' if mot is ivu_gap else '_user_setpoint' - output = '_sum_all' if det is bpm1 else '' - data = np.array(db[uid].table()[[det.name+output, mot.name+sp]])[1:] - - peak_idx = np.argmax(data[:, 0]) - peak_x = data[peak_idx, 1] - peak_y = data[peak_idx, 0] - - if mot is ivu_gap: - m = mot.gap - else: - m = mot - print(f"Found peak for {m.name} at {peak_x} {m.egu} [BPM reading {peak_y}]") - return peak_x, peak_y - -## Lookup tables, Last good positions - - -LUT_fmt = "XF:17ID-ES:FMX{{Misc-LUT:{}}}{}-Wfm" -LGP_fmt = "XF:17ID-ES:FMX{{Misc-LGP:{}}}Pos-SP" - -LUT_valid = (ivu_gap.gap, hdcm.g, hdcm.r, hdcm.p, hfm.y, hfm.x, hfm.pitch, kbm.hy, kbm.vx, atten) -LGP_valid = (kbm.hp, kbm.hx, kbm.vp, kbm.vy) - -LUT_valid_names = [m.name for m in LUT_valid] + ['ivu_gap_off'] -LGP_valid_names = [m.name for m in LGP_valid] - -def get_energy(): - """ - Returns the current photon energy in eV derived from the DCM Bragg angle - """ - - blStr = blStrGet() - if blStr == -1: return -1 - - if blStr == 'AMX': - energy = dcm_amx.e.user_readback.get() - elif blStr == 'FMX': - energy = hdcm.e.user_readback.get() - - return energy - -def read_lut(name): - """ - Reads the LookUp table values for a specific motor - """ - if name not in LUT_valid_names: - raise ValueError('name must be one of {}'.format(LUT_valid_names)) - - x, y = [epics.caget(LUT_fmt.format(name, axis)) for axis in 'XY'] - return pd.DataFrame({'Energy':x, 'Position': y}) - - -def write_lut(name, energy, position): - """ - Writes to the LookUp table for a specific motor - """ - if name not in LUT_valid_names: - raise ValueError('name must be one of {}'.format(LUT_valid_names)) - - if len(energy) != len(position): - raise ValueError('energy and position must have the same number of points') - - epics.caput(LUT_fmt.format(name, 'X'), energy) - epics.caput(LUT_fmt.format(name, 'Y'), position) - - -def read_lgp(name): - """ - Reads the Last Good Position value for a specific motor - """ - if name not in LGP_valid_names: - raise ValueError('name must be one of {}'.format(LGP_valid_names)) - - return epics.caget(LGP_fmt.format(name)) - -def write_lgp(name, position): - """ - Writes to the Last Good Position value for a specific motor - """ - if name not in LGP_valid_names: - raise ValueError('name must be one of {}'.format(LGP_valid_names)) - - return epics.caput(LGP_fmt.format(name), position) - - - -def setE_motors_FMX(energy): - """ - Sets undulator, hdcm, HFM and KB settings for a certain energy from a lookup table - - energy: Photon energy [eV] - - Lookup tables and variables are set in a settings notebook: - settings/set_energy setup FMX.ipynb - - Examples: - setE_motors_FMX(12660) - """ - - # (FMX specific) - LUT = {m: [epics.caget(LUT_fmt.format(m.name, axis)) - for axis in 'XY'] - for m in (ivu_gap.gap, hdcm.g, hdcm.r, hdcm.p, hfm.y, hfm.x, hfm.pitch, kbm.hy, kbm.vx)} - - LUT_offset = [epics.caget(LUT_fmt.format('ivu_gap_off', axis)) for axis in 'XY'] - - LGP = {m: epics.caget(LGP_fmt.format(m.name)) - for m in (kbm.hp, kbm.hx, kbm.vp, kbm.vy)} - - # Remove CRLs if going to energy < 9 keV (FMX specific) - if energy < 9001: - set_beamsize('V0','H0') - - # Lookup Table - def lut(motor): - if motor is ivu_gap: - return motor, np.interp(energy, *LUT[motor.gap]) - else: - return motor, np.interp(energy, *LUT[motor]) - - # Last Good Position - def lgp(motor): - return motor, LGP[motor] - - # (FMX specific) - yield from bps.mv( - *lut(ivu_gap), # Set IVU Gap interpolated position - hdcm.e, energy, # Set Bragg Energy pseudomotor - *lut(hdcm.g), # Set DCM Gap interpolated position - *lut(hdcm.r), # Set DCM Roll interpolated position # MF 20180331 - *lut(hdcm.p), # Set Pitch interpolated position - - # Set HFM from interpolated positions - *lut(hfm.x), - *lut(hfm.y), - *lut(hfm.pitch), - - # Set KB from interpolated positions - *lut(kbm.vx), - *lut(kbm.hy), - - # Set KB from known good setpoints - *lgp(kbm.vy), *lgp(kbm.vp), - *lgp(kbm.hx), *lgp(kbm.hp) - ) - - -def dcm_rock(dcm_p_range=0.03, dcm_p_points=51, logging=True, altDetector=False): - """ - Scan DCM crystal 2 pitch to maximize flux on BPM1 - dcm_rock() runs both with the AMX DCM_AMX and the FMX hdcm - - Parameters - ---------- - - Optional arguments: - dcm_p_range: DCM rocking curve range [mrad]. Default 0.03 mrad - dcm_p_points: DCM rocking curve points. Default 51 - altDetector: If True, uses alternate detector, BPM1 at AMX and Keithley at FMX - - Examples - -------- - - RE(dcm_rock()) - RE(dcm_rock(altDetector = True)) - RE(dcm_rock(dcm_p_range=0.035, dcm_p_points=71)) - """ - blStr = blStrGet() - if blStr == -1: return -1 - - if blStr == 'AMX': - rock_mot = dcm_amx.p - rock_det = bpm1 if altDetector is True else keithley - elif blStr == 'FMX': - rock_mot = hdcm.p - rock_det = keithley if altDetector is True else bpm1 - - energy = get_energy() - - LUT = {m: [epics.caget(LUT_fmt.format(m.name, axis)) - for axis in 'XY'] - for m in (rock_mot, )} - - # Lookup Table - def lut(motor): - return motor, np.interp(energy, *LUT[motor]) - - yield from bps.mv( - *lut(rock_mot) # Set Pitch interpolated position - ) - - # Decorate find_peaks to play along with our plot and plot the peak location - def find_peak_inner(detector, motor, start, stop, num): - if detector == bpm1: - det_name = detector.name+'_sum_all' - else: - det_name = detector.name - mot_name = motor.name+'_user_setpoint' - - # 2DO: Comment out when used within LSDC - def inner(): - peak_x, peak_y = yield from find_peak(detector, motor, start, stop, num) - return peak_x, peak_y - return inner() - - # Scan DCM Pitch - peak_x, peak_y = yield from find_peak_inner(rock_det, rock_mot, -dcm_p_range, dcm_p_range, dcm_p_points) - yield from bps.mv(rock_mot, peak_x) - - # (FMX specific) - if logging: - print('Energy = {:.1f} eV'.format(energy)) - print('hdcm cr2 pitch = {:.3f} mrad'.format(rock_mot.user_readback.get())) - if rock_det == bpm1: - print('BPM1 sum = {:.4g} A'.format(bpm1.sum_all.get())) - elif rock_det == keithley: - time.sleep(2.0) # Range switching is slow - print('Keithley current = {:.4g} A'.format(keithley.get())) - - - - -def ivu_gap_scan(start, end, steps, detector=bpm1, goToPeak=True): - """ - Scans the IVU21 gap against a detector, and moves the gap to the peak plus a - energy dependent look-up table set offset - - Parameters - ---------- - - start: float - The starting position (um) of the VU21 undulator gap scan - - end: float - The end position (um) of the VU21 undulator gap scan - - steps: int - Number of steps in the scan - - detector: ophyd detector - The ophyd detector for the scan. Default is bpm1. Only setup up for the quad BPMs right now - - goToPeak: boolean - If True, go to the peak plus energy-tabulated offset. If False, go back to pre-scan value. - - Examples - -------- - - RE(ivu_gap_scan(7350, 7600, 70)) - RE(ivu_gap_scan(7350, 7600, 70, goToPeak=False)) - RE(ivu_gap_scan(7350, 7600, 70, detector=bpm1)) - """ - - energy = get_energy() - - motor=ivu_gap - if start-1 < motor.gap.low_limit: - start = motor.gap.low_limit + 1 - print('start violates lowest limit, set to %.1f' % start + ' um') - - LUT_offset = [epics.caget(LUT_fmt.format('ivu_gap_off', axis)) for axis in 'XY'] - - # Decorate find_peaks to play along with our plot and plot the peak location - def find_peak_inner(detector, motor, start, stop, num): - det_name = detector.name+'_sum_all' - mot_name = motor.gap.name+'_user_setpoint' if motor is ivu_gap else motor.name+'_user_setpoint' - - # Prevent going below the lower limit or above the high limit - if motor is ivu_gap: - step_size = (stop - start) / (num - 1) - while motor.gap.user_setpoint.get() + start < motor.gap.low_limit: - start += 5*step_size - stop += 5*step_size - - while motor.gap.user_setpoint.get() + stop > motor.gap.high_limit: - start -= 5*step_size - stop -= 5*step_size - - # 2DO: Comment out when used within LSDC - def inner(): - peak_x, peak_y = yield from find_peak(detector, motor, start, stop, num) - return peak_x, peak_y - return inner() - - # Remember pre-scan value - gapPreStart=motor.gap.user_readback.get() - - # Move to start - yield from bps.mv(motor, start) - - # Scan IVU Gap - peak_x, peak_y = yield from find_peak_inner(detector, ivu_gap, 0, (end-start), steps) - - # Go to peak - if goToPeak==True: - peakoffset_x = (peak_x + np.interp(energy, *LUT_offset)) - yield from bps.mv(ivu_gap, peakoffset_x) - print('Gap set to peak + tabulated offset: %.1f' % peakoffset_x + ' um') - else: - yield from bps.mv(ivu_gap, gapPreStart) - print('Gap set to pre-scan value: %.1f' % gapPreStart + ' um') - - -def setELsdc(energy, - dcm_p_range=0.03, dcm_p_points=51, altDetector=False, - ivuGapStartOff=70, ivuGapEndOff=70, ivuGapSteps=31, - transSet='All', beamCenterAlign=True, slit1Set=True): - """ - Automated photon energy change. Master function calling four subroutines: - * setE_motors_FMX(): Set photon delivery system motor positions for a chosen energy - * dcm_rock(): Go to peak of monochromator rocking curve - * ivu_gap_scan(): Go to peak of undulator gap - * beam_center_align(): Set LSDC crosshair to beam center - Move rotation axis to beam heightS - Set governor Gonio Y Work position - - Requirements - ------------ - Governor in state SA - FOE and hutch photon shutter open - - - Parameters - ---------- - - energy: Photon energy [eV] - - dcm_p_range: Scan range of DCM Crystal 2 Pitch [mrad], default = 0.03 - dcm_p_points: Number of scan points of SCM rocking curve, default = 51 - altDetector: Rocking curve to use alternate detector between BPM1 and endstation diode, default = False - - ivuGapStartOff: IVU gap scan start offset from tabulated position [um], default = 70 - ivuGapEndOff: IVU gap scan end offset from tabulated position [um], default = 70 - ivuGapSteps: IVU gap scan steps, default = 31 - - transSet: FMX only: Set to 'RI' if there is a problem with the BCU attenuator. - FMX only: Set to 'BCU' if there is a problem with the RI attenuator. - Set to 'None' if there are problems with all = attenuators. - Operator then has to choose a flux by hand that will not saturate scinti - default = 'All' - - beamCenterAlign: Set to False to skip beam_center_align() step (like the old set_energy() routine) - Default True - - slit1Set: Set to False to skip setting Slit 1 Gap values - Default True - - Examples - -------- - - RE(setE(7110)) - RE(setE(10000, transSet='None')) - RE(setE(12660, transSet='RI')) - RE(setE(20000, ivuGapStartOff=100, ivuGapEndOff=150, ivuGapSteps=91)) - RE(setE(9000, beamCenterAlign=False)) - RE(setE(12660, beamCenterAlign=False, slit1Set=False)) - """ - - desired_states = ("SA", "AB") - if not gov_robot.state.get() in desired_states: - print(f'Governor state not in one of {desired_states}, exiting') - return -1 - - # Store initial Slit 1 gap positions - if slit1Set: - slits1XGapOrg = slits1.x_gap.user_readback.get() - slits1YGapOrg = slits1.y_gap.user_readback.get() - - print('Setting FMX motor positions') - try: - yield from setE_motors_FMX(energy) - except: - print('setE_motors_FMX() failed') - raise - else: - print('setE_motors_FMX() successful') - time.sleep(1) - - # Check for pre-conditions for dcm_rock() and ivu_gap_scan() - if shutter_foe.status.get(): - print('FOE shutter closed. Has to be open for this to work. Exiting') - return -1 - - # DCM rocking curve - print('Rocking monochromator') - yield from dcm_rock(dcm_p_range=dcm_p_range, dcm_p_points=dcm_p_points, altDetector=altDetector) - time.sleep(1) - - # Undulator gap scan - print('Scanning undulator gap') - start = ivu_gap.gap.user_readback.get() - ivuGapStartOff - end = ivu_gap.gap.user_readback.get() + ivuGapEndOff - try: - yield from ivu_gap_scan(start, end, ivuGapSteps, goToPeak=True) - except: - print('ivu_gap_scan() failed') - raise - else: - print('ivu_gap_scan() successful') - time.sleep(1) - - # Activate sector 17 photon local feedback - photon_local_feedback_c17.x_enable.put(1) - photon_local_feedback_c17.y_enable.put(1) - - # Align LSDC microscope center to beam center - if beamCenterAlign: - # Check for pre-conditions for beam_center_align() - if shutter_hutch_c.status.get(): - print('Experiment hutch shutter closed. Has to be open for this to work. Exiting') - return -1 - - print('Aligning beam center') - yield from beam_center_align(transSet=transSet) - - # Restore initial Slit 1 gap positions - if slit1Set: - yield from bps.mv(slits1.x_gap, slits1XGapOrg) # Move Slit 1 X to original position - yield from bps.mv(slits1.y_gap, slits1YGapOrg) # Move Slit 1 Y to original position - - yield from fmx_reference(transSet=transSet) - - -# Alignment =========================================================================================== - -## Plans to align beam and goniometer for LSDC - -def centroid_avg(stats): - """ - Read centroid X and Y 10x and return mean of centroids. - - stats : stats method of ophyd camera object to use, e.g. cam_8.stats4 - - Examples - -------- - centroid_avg(cam_8.stats4) - centroidY = centroid_avg(cam_8.stats4)[1] - """ - - centroidXArr = np.zeros(10) - centroidYArr = np.zeros(10) - for i in range(0, 10): - centroidXArr[i] = stats.centroid.x.get() - centroidYArr[i] = stats.centroid.y.get() - # print('Centroid X = {:.6g} px'.format(centroidXArr[i]), ', Centroid Y = {:.6g} px'.format(centroidYArr[i])) - time.sleep(0.2) - CentroidX = centroidXArr.mean() - CentroidY = centroidYArr.mean() - print('Mean centroid X = {:.6g} px'.format(CentroidX)) - print('Mean centroid Y = {:.6g} px'.format(CentroidY)) - - return CentroidX, CentroidY - - -def detectorCoverClose(): - """ - Closes the Detector Cover - """ - yield from bps.mv(cover_detector.close, 1) - - while cover_detector.status.get() == 1: - #print(cover_detector.status.get()) - time.sleep(0.5) - - return - -def detectorCoverOpen(): - """ - Opens the Detector Cover - """ - yield from bps.mv(cover_detector.open, 1) - - while cover_detector.status.get() != 1: - #print(cover_detector.status.get()) - time.sleep(0.5) - - return - - -def trans_set(transmission, trans = trans_bcu): - """ - Sets the Attenuator transmission - """ - - e_dcm = get_energy() - if e_dcm < 5000 or e_dcm > 30000: - print('Monochromator energy out of range. Must be within 5000 - 30000 eV. Exiting.') - return - - yield from bps.mv(trans.energy, e_dcm) # This energy PV is only used for debugging - yield from bps.mv(trans.transmission, transmission) - yield from bps.mv(trans.set_trans, 1) - - if trans == trans_bcu: - while atten_bcu.done.get() != 1: - time.sleep(0.5) - - print('Attenuator = ' + trans.name + ', Transmission set to %.3f' % trans.transmission.get()) - return - - -def trans_get(trans = trans_bcu): - """ - Returns the Attenuator transmission - """ - - transmission = trans.transmission.get() - - print('Attenuator = ' + trans.name + ', Transmission = %.3f' % transmission) - return transmission - - -def transDefaultGet(energy): - """ - Returns the default transmission to avoid saturation of the scintillator - - energy: X-ray energy [eV] - - The look up table is set in settings/set_energy setup FMX.ipynb - """ - - # This reads from: - # XF:17ID-ES:FMX{Misc-LUT:atten}X-Wfm - # XF:17ID-ES:FMX{Misc-LUT:atten}Y-Wfm - # - # atten is a dummy motor just for this purpose. - # To be replaced by trans_bcu and corresponding new PVs - - transLUT = read_lut('atten') - transDefault = np.interp(energy,transLUT['Energy'],transLUT['Position']) - - return transDefault - - -# Beam align functions - -def beam_center_align(transSet='All'): - """ - Corrects alignment of goniometer and LSDC center point after a beam drift - - Requirements - ------------ - * No sample mounted. Goniometer will be moved inboard out of sample position - * Governor in SA state - - Parameters - ---------- - transSet: FMX only: Set to 'RI' if there is a problem with the BCU attenuator. - FMX only: Set to 'BCU' if there is a problem with the RI attenuator. - Set to 'None' if there are problems with all attenuators. - Operator then has to choose a flux by hand that will not saturate scinti - default = 'All' - - Examples - -------- - RE(beam_center_align()) - RE(beam_center_align(transSet='None')) - RE(beam_center_align(transSet='RI')) - """ - # TODO: - # * Consider running with BCU attenuators only - # * Check for Vis screen actuators out - # * Check for C-hutch shutter open - # * Check for ROI2 exceeding camera border. - # Check how this affects the ROI centering move, and whether we can correct for that - - # Which beamline? - blStr = blStrGet() - if blStr == -1: return -1 - - if blStr == 'FMX': - if transSet not in ['All', 'None', 'BCU', 'RI']: - print('transSet must be one of: All, None, BCU, RI') - return -1 - else: - if transSet not in ['All', 'None']: - print('transSet must be one of: All, None') - return -1 - - desired_states = ("SA", "AB") - if not gov_robot.state.get() in desired_states: - print(f'Governor state not in one of {desired_states}, exiting.') - return -1 - - # Check for beam after DCM: BPM1 total current - if bpm1.sum_all.get() < 1e-7: - print('Intensity after DCM low. BPM1 total current <1e-7 A.', - 'Check FOE shutter, and rocking curve, then repeat.', - 'Exiting.') - return -1 - - print('Closing detector cover') - detectorCoverClose() - - # Transition to Governor state AB (Auto-align Beam) - gov_lib.setGovRobot(gov_robot, 'AB') - - # Set beam transmission that avoids scintillator saturation - # Default values are defined in settings as lookup table - if transSet != 'None': - transDefault = transDefaultGet( get_energy() ) - if blStr == 'FMX': - if transSet in ['All', 'BCU']: - transOrgBCU = trans_get(trans=trans_bcu) - if transSet in ['All', 'RI']: - transOrgRI = trans_get(trans=trans_ri) - yield from trans_set(transDefault, trans=trans_ri) - if transSet == 'BCU': - yield from trans_set(transDefault, trans=trans_bcu) - if transSet == 'All': - yield from trans_set(1, trans=trans_bcu) - else: - transOrgBCU = trans_get(trans=trans_bcu) - yield from trans_set(transDefault, trans=trans_bcu) - - # Retract backlight - yield from bps.mv(light.y,govs.gov.Robot.dev.li.target_Out.get()) - print('Light Y Out') - - # TODO: use "yield from bps.mv(...)" instead of .put(...) below. - - # ROI1 centroid plugin does not work - # Copy ROI1 geometry to ROI4 and use ROI4 centroid plugin - cam_8.roi4.min_xyz.min_x.put(cam_8.roi1.min_xyz.min_x.get()) - cam_8.roi4.min_xyz.min_y.put(cam_8.roi1.min_xyz.min_y.get()) - cam_8.roi4.size.x.put(cam_8.roi1.size.x.get()) - cam_8.roi4.size.y.put(cam_8.roi1.size.y.get()) - - yield from bps.mv(shutter_bcu.open, 1) - print('BCU Shutter Open') - time.sleep(1) - - # Check for focused beam on scinti. Do nothing if stats 4 max intensity < 20 counts - # TODO: Verify 20 counts threshold for more settings - if cam_8.stats4.max_value.get() < 20: - print('Max intensity < 20 counts.', - 'Check beam intensity and focus on scinti, then repeat.', - 'No changes made.') - else: - # Camera calibration [um/px] - hiMagCal = BL_calibration.HiMagCal.get() - loMagCal = BL_calibration.LoMagCal.get() - - # Read centroids - beamHiMagCentroid = centroid_avg(cam_8.stats4) - beamHiMagCentroidX = beamHiMagCentroid[0] - beamHiMagCentroidY = beamHiMagCentroid[1] - time.sleep(1) - - # Get beam shift on Hi Mag - # Assume the LSDC centering crosshair is in the center of the FOV - # This works as long as cam_8 ROI1 does not hit the edge of the cam_8 image - beamHiMagDiffX = beamHiMagCentroidX - (cam_8.roi4.size.x.get()/2) - beamHiMagDiffY = beamHiMagCentroidY - (cam_8.roi4.size.y.get()/2) - - # Do nothing if we see a too large shift - if beamHiMagDiffX>100 or beamHiMagDiffY>100: - print('Beam centroid change > 100 px detected.', - 'No changes made. Manual beam center correction needed.') - beamHiMagDiffX=0 - beamHiMagDiffY=0 - - - # Correct Mag 4 (cam_8 ROI1) - # Adjust cam_8 ROI1 min_y, LSDC uses this for the Mag4 FOV. - cam_8.roi1.min_xyz.min_x.put(cam_8.roi1.min_xyz.min_x.get() + beamHiMagDiffX) - cam_8.roi1.min_xyz.min_y.put(cam_8.roi1.min_xyz.min_y.get() + beamHiMagDiffY) - - # Correct Mag 3 (cam_8 ROI2) - # This works as long as cam_8 ROI2 does not hit the edge of the cam_8 image - cam_8.roi2.min_xyz.min_x.put(cam_8.roi2.min_xyz.min_x.get() + beamHiMagDiffX) - cam_8.roi2.min_xyz.min_y.put(cam_8.roi2.min_xyz.min_y.get() + beamHiMagDiffY) - - # Get beam shift on Lo Mag from Hi Mag shift and calibration factor ratio - beamLoMagDiffX = beamHiMagDiffX * hiMagCal/loMagCal - beamLoMagDiffY = beamHiMagDiffY * hiMagCal/loMagCal - - # Correct Mag 1 (cam_7 ROI2) - cam_7.roi2.min_xyz.min_x.put(cam_7.roi2.min_xyz.min_x.get() + beamLoMagDiffX) - cam_7.roi2.min_xyz.min_y.put(cam_7.roi2.min_xyz.min_y.get() + beamLoMagDiffY) - - # Correct Mag 2 (cam_7 ROI3) - cam_7.roi3.min_xyz.min_x.put(cam_7.roi3.min_xyz.min_x.get() + beamLoMagDiffX) - cam_7.roi3.min_xyz.min_y.put(cam_7.roi3.min_xyz.min_y.get() + beamLoMagDiffY) - - time.sleep(3) - - # Adjust Gonio Y so rotation axis is again aligned to beam - gonioYDiff = beamHiMagDiffY * hiMagCal - posGyOld = govs.gov.Robot.dev.gy.target_Work.get() - posGyNew = posGyOld + gonioYDiff - yield from bps.mv(gonio.gy, posGyNew) # Move Gonio Y to new position - govs.gov.Robot.dev.gy.target_Work.set(posGyNew) # Set Governor Gonio Y Work position to new value - print('Gonio Y difference = %.3f' % gonioYDiff) - - yield from bps.mv(shutter_bcu.close, 1) - print('BCU Shutter Closed') - - # Transition to Governor state SA (Sample Alignment) - gov_lib.setGovRobot(gov_robot, 'SA') - - # Set previous beam transmission - if transSet != 'None': - if blStr == 'FMX': - if transSet in ['All', 'RI']: - yield from trans_set(transOrgRI, trans=trans_ri) - if transSet in ['All', 'BCU']: - yield from trans_set(transOrgBCU, trans=trans_bcu) - else: - yield from trans_set(transOrgBCU, trans=trans_bcu) - -def get_fluxKeithley(): - """ - Returns Keithley diode current derived flux. - """ - - keithFlux = epics.caget('XF:17IDA-OP:FMX{Mono:DCM-dflux}') - - return keithFlux - - -def set_fluxBeam(flux): - """ - Sets the flux reference field. - - flux: Beamline flux at sample position for transmisison T = 1. [ph/s] - """ - - error = epics.caput('XF:17IDA-OP:FMX{Mono:DCM-dflux-M}', flux) - - return error - - -def slit1_flux_reference(flux_df,slit1Gap): - """ - Sets Slit 1 X gap and Slit 1 Y gap to a specified position, - and returns flux reference values to a provided pandas DataFrame. - - Supporting function for fmx_flux_reference() - - Parameters - ---------- - - slit1Gap: float - Gap value for Slit 1 X and Y [um] - - flux_df: pandas DataFrame with fields: - Slit 1 X gap [um] - Slit 1 Y gap [um] - Keithley current [A] - Keithley flux [ph/s] - BPM1 sum [A] - BPM4 sum [A] - - """ - yield from bps.mv(slits1.x_gap, slit1Gap, slits1.y_gap, slit1Gap, wait=True) - time.sleep(2.0) # wait so the slits are definitely done moving and the Keithley reading is stable - - flux_df.at[slit1Gap, 'Slit 1 X gap [um]'] = slit1Gap - flux_df.at[slit1Gap, 'Slit 1 Y gap [um]'] = slit1Gap - flux_df.at[slit1Gap, 'Keithley current [A]'] = keithley.get() - flux_df.at[slit1Gap, 'Keithley flux [ph/s]'] = get_fluxKeithley() - flux_df.at[slit1Gap, 'BPM1 sum [A]'] = bpm1.sum_all.get() - # TEMP FIX: flux_df.at[slit1Gap, 'BPM4 sum [A]'] = bpm4.sum_all.get() - flux_df.at[slit1Gap, 'BPM4 sum [A]'] = 0 - -def fmx_flux_reference(slit1GapList = [2000, 1000, 600, 400], slit1GapDefault = 1000, transSet='All'): - """ - Sets Slit 1 X gap and Slit 1 Y gap to a list of settings, - and stores flux reference values in a global pandas DataFrame. - - Parameters - ---------- - - slit1GapList: float (default=[2000, 1000, 600, 400]) - A list of gap values [um] for Slit 1 X and Y - slit1GapDefault: Gap value [um] to set as default after getting references - Default slit1GapDefault = 1000 - - Returns - ------- - - flux_df: pandas DataFrame with fields - Slit 1 X gap [um] - Slit 1 Y gap [um] - Keithley current [A] - Keithley flux [ph/s] - BPM1 sum [A] - BPM4 sum [A] - - Examples - -------- - fmx_flux_reference() - flux_df=fmx_flux_reference() - flux_df - fmx_flux_reference(slit1GapList = [2000, 1500, 1000]) - fmx_flux_reference(slit1GapList = [2000, 1500, 1000], slit1GapDefault = 600) - - """ - - # Store current transmission, then set full transmission - if transSet != 'None': - if transSet in ['All', 'BCU']: - transOrgBCU = trans_get(trans=trans_bcu) - if transSet in ['All', 'RI']: - transOrgRI = trans_get(trans=trans_ri) - yield from trans_set(1.0, trans=trans_ri) - if transSet == 'BCU': - yield from trans_set(1.0, trans=trans_bcu) - if transSet == 'All': - yield from trans_set(1.0, trans=trans_bcu) - - msgStr = "Energy = " + "%.1f" % get_energy() + " eV" - print(msgStr) - logger.info(msgStr) - - global flux_df - flux_df = pd.DataFrame(columns=['Slit 1 X gap [um]', - 'Slit 1 Y gap [um]', - 'Keithley current [A]', - 'Keithley flux [ph/s]', - 'BPM1 sum [A]', - 'BPM4 sum [A]', - ]) - - # Put in diode - yield from bps.mv(light.y, - govs.gov.Robot.dev.li.target_Diode.get()) - - - # Open BCU shutter - yield from bps.mv(shutter_bcu.open, 1) - time.sleep(1) - - for slit1Gap in slit1GapList: - yield from slit1_flux_reference(flux_df,slit1Gap) - - # Move back to default slit width - # TODO: save reference before and return to - yield from bps.mv(slits1.x_gap, slit1GapDefault, slits1.y_gap, slit1GapDefault, wait=True) - time.sleep(2.0) # wait so the slits are definitely done moving and the Keithley reading is stable - - vFlux = get_fluxKeithley() - set_fluxBeam(vFlux) - msgStr = "Reference flux for Slit 1 gap = " + "%d" % slit1GapDefault + " um for T=1 set to " + "%.1e" % vFlux + " ph/s" - print(msgStr) - logger.info(msgStr) - - - # Close shutter - yield from bps.mv(shutter_bcu.close, 1) - - # Retract diode - yield from bps.mv(light.y, - govs.gov.Robot.dev.li.target_In.get()) - - # Set previous beam transmission - if transSet != 'None': - if transSet in ['All', 'RI']: - yield from trans_set(transOrgRI, trans=trans_ri) - if transSet in ['All', 'BCU']: - yield from trans_set(transOrgBCU, trans=trans_bcu) - - -def fmx_reference(slit1GapDefault = 1000, transSet='All'): - """ - Calls fmx_flux_reference, then fmx_beamline_reference. - setE will do the same after the beam alignment. - - Parameters - ---------- - transSet: FMX only: Set to 'RI' if there is a problem with the BCU attenuator. - FMX only: Set to 'BCU' if there is a problem with the RI attenuator. - Set to 'None' if there are problems with all attenuators. - Operator then has to choose a flux by hand that will not saturate scinti - default = 'All' - - Examples - -------- - fmx_reference() - - """ - if not gov_robot.state.get() == "SA": - print('Not in Governor state SA, exiting') - return - - # Transition to Governor state BL - gov_lib.setGovRobot(gov_robot, 'BL') - - yield from fmx_flux_reference(slit1GapDefault = slit1GapDefault, transSet = transSet) - - # Transition to Governor state SA - gov_lib.setGovRobot(gov_robot, 'SA') - - log_fmx_beamline_reference() - - - -def log_fmx_beamline_reference(): - """ - Prints reference values and appends to the FMX log file - - - Examples - -------- - fmx_beamline_reference() - - """ - global flux_df - messages = [ - f"Energy = {get_energy():.2f} eV", - f"Beam current = {beam_current.get():.2f} mA", - f"IVU gap = {ivu_gap.gap.user_readback.get():.1f} um", - f"XBPM2 posX = {xbpm2.x.get():.2f} um", - f"XBPM2 posY = {xbpm2.y.get():.2f} um", - f"XBPM2 total current = {xbpm2.total.get():.2f} uA", - f"HDCM pitch = {hdcm.p.user_readback.get():.4f} mrad", - f"HDCM roll = {hdcm.r.user_readback.get():.4f} mrad", - f"BPM1 posX = {bpm1.x.get():.2f}", - f"BPM1 posY = {bpm1.y.get():.2f}", - f"BPM1 total current = {bpm1.sum_all.get():.3f} A", - f"HFM pitch = {hfm.pitch.user_readback.get():.4f} mrad", - f"VKB tweak voltage = {vkb_piezo_tweak.get():.3f} V", - f"VKB pitch = {kbm.vp.user_readback.get():.4f} urad", - f"HKB tweak voltage = {hkb_piezo_tweak.get():.3f} V", - f"HKB pitch = {kbm.hp.user_readback.get():.4f} urad", - f"Gonio X = {gonio.gx.user_readback.get():.1f} um", - f"Gonio Y = {gonio.gy.user_readback.get():.1f} um", - f"Gonio Z = {gonio.gz.user_readback.get():.1f} um", - ] - for message in messages: - print(message) - logger.info(message) - if flux_df is not None: - logger.info(f"Flux dataframe {flux_df.to_string()}") diff --git a/top_view.py b/top_view.py index 5e235d81..4c3c732a 100644 --- a/top_view.py +++ b/top_view.py @@ -6,7 +6,6 @@ import os import filecmp import logging -import subprocess from config_params import TOP_VIEW_CHECK logger = logging.getLogger(__name__) @@ -36,10 +35,9 @@ def wait90TopviewThread(gov_robot, prefix1,prefix90): snapshot1Name = prefix1+"_001.jpg" snapshot2Name = prefix90+"_001.jpg" if (not filecmp.cmp(getBlConfig("visitDirectory")+"/pinAlign/"+snapshot1Name,getBlConfig("visitDirectory")+"/pinAlign/"+snapshot2Name)): #this would mean something is wrong if true because the pictures are identical - comm_s = [os.environ["LSDCHOME"] + "/runPinAlign.py", snapshot1Name, snapshot2Name] + comm_s = os.environ["LSDCHOME"] + "/runPinAlign.py " + snapshot1Name + " " + snapshot2Name logger.info(comm_s) - process = subprocess.run(comm_s, cwd=getBlConfig("visitDirectory"), stdout=subprocess.PIPE, stderr=subprocess.PIPE) - lines = process.stdout.decode().strip().split('\n') + lines = os.popen(comm_s, cwd=getBlConfig("visitDirectory")).readlines() logger.info("printing lines right after popen ") logger.info(lines) logger.info(" done") diff --git a/utils/healthcheck.py b/utils/healthcheck.py index 3bdc105e..1e569766 100644 --- a/utils/healthcheck.py +++ b/utils/healthcheck.py @@ -168,14 +168,6 @@ def check_curr_visit_dir() -> bool: return True -@healthcheck(name="check environment variables", remediation="", fatal=True) -def check_env_variables() -> bool: - env_vars = ["STAFF_GROUP", "NSLS2_API_URL"] - missing_vars = [var for var in env_vars if var not in os.environ] - if missing_vars: - check_env_variables.remidiation = f"Environment variable(s) not found: {','.join(missing_vars)}" - return False - return True @healthcheck(name="existence of environment file", remediation="", fatal=True) def check_env_file() -> bool: From 1ec7ed5ca83144e6af7782de4ba7434c586637fd Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra <133771854+NYSBC-Rudra@users.noreply.github.com> Date: Wed, 8 Nov 2023 15:22:22 -0500 Subject: [PATCH 65/79] Adding resolution drop down to file menu Final for now until testing --- gui/control_main.py | 40 +++++ gui/dialog/__init__.py | 2 + gui/dialog/resolution_calculator.py | 97 ++++++++++++ gui/dialog/resolution_dialog.py | 230 ++++++++++++++++++++++++++++ 4 files changed, 369 insertions(+) create mode 100644 gui/dialog/resolution_calculator.py create mode 100644 gui/dialog/resolution_dialog.py diff --git a/gui/control_main.py b/gui/control_main.py index 59dfd5cb..e542f8fe 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -50,6 +50,7 @@ SnapCommentDialog, StaffScreenDialog, UserScreenDialog, + CalculatorWindow ) from gui.raster import RasterCell, RasterGroup from QPeriodicTable import QPeriodicTable @@ -270,6 +271,9 @@ def parseXRFTable(self): XRFInfoDict[tokens[0]] = int(float(tokens[5]) * 100) XRFFile.close() return XRFInfoDict + + + def closeEvent(self, evnt): evnt.accept() @@ -2725,6 +2729,10 @@ def popBaseDirectoryDialogCB(self): ) if fname != "": self.dataPathGB.setBasePath_ledit(fname) + ''' + Creating function to for Actions under file drop down + + ''' def popImportDialogCB(self): #self.timerSample.stop() @@ -2747,6 +2755,14 @@ def setUserModeCB(self): def setExpertModeCB(self): self.vidActionDefineCenterRadio.setEnabled(True) + + + def openResolution(self): + self.sub = CalculatorWindow(self) + self.sub.show() + + + def upPriorityCB( self, @@ -5089,6 +5105,26 @@ def initUI(self): splitter1.addWidget(self.tabs) self.setCentralWidget(splitter1) splitterSizes = [600, 100] + + ''' + creating drop down menu items under File + + for now has + importAction - importing spreadsheet manually + @function - popImportDialogCB + + userAction - User Mode + @function - setUserModeCB + + expertAction - Expert Mode + @function - setExpertModeCB + + staffAction - Staff Panel + @function - popStaffDialogCB + + resolutionAction - Resolution Calculator + @function - openResolution + ''' importAction = QtWidgets.QAction("Import Spreadsheet...", self) importAction.triggered.connect(self.popImportDialogCB) modeGroup = QtWidgets.QActionGroup(self) @@ -5100,6 +5136,8 @@ def initUI(self): self.expertAction.triggered.connect(self.setExpertModeCB) self.staffAction = QtWidgets.QAction("Staff Panel...", self) self.staffAction.triggered.connect(self.popStaffDialogCB) + self.resolutionAction = QtWidgets.QAction("Resolution Calculator", self) + self.resolutionAction.triggered.connect(self.openResolution) modeGroup.addAction(self.userAction) modeGroup.addAction(self.expertAction) exitAction = QtWidgets.QAction(QtGui.QIcon("exit24.png"), "Exit", self) @@ -5109,6 +5147,7 @@ def initUI(self): self.statusBar() self.queue_collect_status_widget = QtWidgets.QLabel("Queue Collect: ON") self.statusBar().addPermanentWidget(self.queue_collect_status_widget) + menubar = self.menuBar() fileMenu = menubar.addMenu("&File") settingsMenu = menubar.addMenu("Settings") @@ -5116,6 +5155,7 @@ def initUI(self): fileMenu.addAction(self.userAction) fileMenu.addAction(self.expertAction) fileMenu.addAction(self.staffAction) + fileMenu.addAction(self.resolutionAction) # Define all of the available actions for the overlay color group self.BlueOverlayAction = QtWidgets.QAction("Blue", self, checkable=True) self.RedOverlayAction = QtWidgets.QAction("Red", self, checkable=True) diff --git a/gui/dialog/__init__.py b/gui/dialog/__init__.py index 4a490c57..653c2b05 100644 --- a/gui/dialog/__init__.py +++ b/gui/dialog/__init__.py @@ -5,3 +5,5 @@ from .puck_dialog import PuckDialog from .dewar import DewarDialog from .screen_defaults import ScreenDefaultsDialog +from .resolution_calculator import Calculator +from .resolution_dialog import CalculatorWindow diff --git a/gui/dialog/resolution_calculator.py b/gui/dialog/resolution_calculator.py new file mode 100644 index 00000000..394f9ab9 --- /dev/null +++ b/gui/dialog/resolution_calculator.py @@ -0,0 +1,97 @@ +import math +import sys + + +class Calculator: + """ + Make a calculator object that can calculate resolution formulas (and nothing else) + + """ + def __init__(self): + self.r = None + self.d = None + self.L = None + self.theta = None + self.wavelength = None + + def set_all_variables(self, variable_dict): + for key in variable_dict: + self.set_variables(key, variable_dict[key]) + + + + def set_variables(self, name, value): + if name == 'r': + self.r = value + elif name == 'd': + self.d = value + elif name == 'L': + self.L = value + elif name == 'theta': + self.theta = value + elif name == 'wavelength': + self.wavelength = value + + def calcD(self, r = None, L = None, wavelength = None, theta = None): + r = r or self.r + L = L or self.L + wavelength = wavelength or self.wavelength + theta = theta or self.theta + try: + denominator = 2*(math.sin((0.5*math.atan(r/L)) + theta)) + numerator = wavelength + return numerator/denominator + except Exception as e: + return e + + + + + def calcL(self, r = None, d = None, wavelength = None, theta = None): + r = r or self.r + d = d or self.d + wavelength = wavelength or self.wavelength + theta = theta or self.theta + try: + denominator = math.tan((2* math.asin(wavelength/(2*d) ) ) -(2*theta) ) + numerator = r + return numerator/denominator + except Exception as e: + return e + + + def calcTheta(self, r = None, L = None, wavelength = None, d = None): + r = r or self.r + L = L or self.L + wavelength = wavelength or self.wavelength + d = d or self.d + try: + val1 = math.asin(wavelength/(2*d)) + val2 = 0.5*math.atan(r/L) + return val1 - val2 + except Exception as e: + return e + + + def calcWavelength(self, r = None, L = None, d = None, theta = None): + r = r or self.r + L = L or self.L + d = d or self.d + theta = theta or self.theta + valin = 0.5*math.atan(r/L) + try: + wavelength = 2*d * math.sin(valin + theta) + return wavelength + except Exception as e: + return e + + + + + + + + + + + \ No newline at end of file diff --git a/gui/dialog/resolution_dialog.py b/gui/dialog/resolution_dialog.py new file mode 100644 index 00000000..f00d6445 --- /dev/null +++ b/gui/dialog/resolution_dialog.py @@ -0,0 +1,230 @@ +from qtpy.QtWidgets import * +from qtpy import QtCore +from qtpy import QtGui +from qtpy import QtWidgets +from qtpy.QtCore import * +from qtpy.QtGui import * +import sys +from gui.dialog import Calculator +import typing + +if typing.TYPE_CHECKING: + from lsdcGui import ControlMain + +WINDOW_SIZE = 480 + +#main qtpy window the calculator exists in + + +class CalculatorWindow(QtWidgets.QDialog): + def __init__(self, parent: "ControlMain"): + super(CalculatorWindow, self).__init__(parent) + self.setFixedSize(WINDOW_SIZE,WINDOW_SIZE) + #making radio buttons to choose formula + self.buttonDictionary = {'L': {'picker' : QRadioButton('Caluclate crystal to detector distance')}, + 'd': {'picker': QRadioButton("Calculate resolution")} , + 'theta': {'picker':QRadioButton("Calculate detector 2theta")}, + 'wavelength': {'picker':QRadioButton("Calculate wavelength")}, + 'r':{'value':None}} + + #making lines to hold inputs + self.r_value_enter = QComboBox() + self.r_value_enter.setToolTip("Detector Distance") + self.r_value_enter = QLineEdit() + self.r_value_enter.setPlaceholderText('Set r value (in mm)') + self.buttonDictionary['r']['value'] = self.r_value_enter + self.r_value_enter.setCurrentIndex(1) + #setting inputs to Double only + + self.L_value_enter = QLineEdit() + self.L_value_enter.setPlaceholderText('Set L value') + self.buttonDictionary['L']['value'] = self.L_value_enter + self.L_value_enter.setValidator(QDoubleValidator()) + + self.d_value_enter = QLineEdit() + self.d_value_enter.setPlaceholderText('Set d value') + self.buttonDictionary['d']['value'] = self.d_value_enter + self.d_value_enter.setValidator(QDoubleValidator()) + + self.theta_value_enter = QLineEdit() + self.theta_value_enter.setPlaceholderText('Set theta value') + self.buttonDictionary['theta']['value'] = self.theta_value_enter + self.theta_value_enter.setValidator(QDoubleValidator()) + + self.wave_value_enter = QLineEdit() + self.wave_value_enter.setPlaceholderText('Set wavelength value') + self.buttonDictionary['wavelength']['value'] = self.wave_value_enter + self.wave_value_enter.setValidator(QDoubleValidator()) + + + self.final_button = QPushButton('Calculate', self) + self.final_button.clicked.connect(self.calculateValue) + + self.bottom_text = QLabel() + self.bottom_text.setText('Enter values and Press button to calculate') + + + #creating calculator object + self.calculator = Calculator() + + + + layout = QVBoxLayout() + layout.addWidget(self.r_value_enter) + for key in self.buttonDictionary: + if 'picker' in self.buttonDictionary[key].keys(): + layout.addWidget(self.buttonDictionary[key]['picker']) + layout.addWidget(self.buttonDictionary[key]['value']) + layout.addWidget(self.final_button) + layout.addWidget(self.bottom_text) + self.setLayout(layout) + #self._createDisplay() + + + + # def _createButtons(self): + # buttonsLayout = QGridLayout() + # self.formula_picker = QRadioButton('Formula') + # self.b2 = QRadioButton("Button2") + + ''' + calls resolution calculator to calculate value depending on inputs from widgets + + -outputs + -value_to_return = value from formula calculated if no problems + -returns nothing if a problem occured, changes bottom_text + ''' + + def calculateValue(self): + checked_key = None + #checking which formula to use + for key in self.buttonDictionary: + if key != 'r' and self.buttonDictionary[key]['picker'].isChecked(): + checked_key = key + if checked_key == None: + self.bottom_text.setText("No calculation specified (press one of the radio buttons)") + return + + #getting values from textboxes r_value text box + r_value = self.r_value_enter.currentIndex() + convertValues = [200,244.7] + # print("r value = {}".format(r_value)) + #checking if value is a number string or empty string + r_value = convertValues[r_value] + r_value = float(r_value) + + + + d_value = self.d_value_enter.displayText() + #checking if value is string or none if not calculating that value (trying to use .isalpha but not when value is None) + if ((d_value == "" or d_value[0].isalpha() == True) and checked_key != 'd') : + self.bottom_text.setText("formula to calculate {} requires d value".format(checked_key)) + return + + l_value = self.L_value_enter.displayText() + if ((l_value == "" or l_value[0].isalpha() == True) and checked_key != 'L'): + self.bottom_text.setText("formula to calculate {} requires L value".format(checked_key)) + return + + theta_value = self.theta_value_enter.displayText() + if ((theta_value == "" or theta_value[0].isalpha() == True)and checked_key != 'theta'): + self.bottom_text.setText("formula to calculate {} requires theta value".format(checked_key)) + return + + wave_value = self.wave_value_enter.displayText() + if ((wave_value == "" or wave_value[0].isalpha() == True) and checked_key != 'wavelength'): + self.bottom_text.setText("formula to calculate {} requires the wavelenght".format(checked_key)) + return + + + #setting value to return if want value returned + value_to_return = None + + if checked_key == 'd': + l_value = float(self.L_value_enter.displayText()) + theta_value = float(self.theta_value_enter.displayText()) + wave_value = float(self.wave_value_enter.displayText()) + + + + + + variableDict = {'L':l_value, 'theta': theta_value, 'wavelength': wave_value, 'r': r_value} + + self.calculator.set_all_variables(variableDict) + d_value = self.calculator.calcD() + value_to_return = d_value + self.d_value_enter.setText(str(d_value)) + self.calculator.set_variables('d', d_value) + + + + + elif checked_key == 'L': + + D_value = float(self.D_value_enter.displayText()) + theta_value = float(self.theta_value_enter.displayText()) + wave_value = float(self.wave_value_enter.displayText()) + + + + + variableDict = {'d':d_value, 'theta': theta_value, 'wavelength': wave_value, 'r': r_value} + + self.calculator.set_all_variables(variableDict) + L_value = self.calculator.calcL() + value_to_return = L_value + self.L_value_enter.setText(str(L_value)) + self.calculator.set_variables('L', L_value) + + elif checked_key == 'theta': + + l_value = float(self.L_value_enter.displayText()) + d_value = float(self.d_value_enter.displayText()) + wave_value = float(self.wave_value_enter.displayText()) + + + variableDict = {'L':l_value, 'd': d_value, 'wavelength': wave_value, 'r': r_value} + + self.calculator.set_all_variables(variableDict) + theta_value = self.calculator.calcTheta() + value_to_return = theta_value + self.theta_value_enter.setText(str(theta_value)) + self.calculator.set_variables('theta', theta_value) + + + + elif checked_key == 'wavelength': + + l_value = float(self.L_value_enter.displayText()) + theta_value = float(self.theta_value_enter.displayText()) + d_value = float(self.d_value_enter.displayText()) + variableDict = {'L':l_value, 'd': d_value, 'theta': theta_value, 'r': r_value} + + self.calculator.set_all_variables(variableDict) + wave_value = self.calculator.calcWavelength() + self.calculator.set_variables('wavelength', wave_value) + value_to_return = wave_value + self.wave_value_enter.setText(str(wave_value)) + + + + + + + + + self.bottom_text.setText("- Done Calculating - \n {} value = {}".format(checked_key, value_to_return)) + return value_to_return + + + + + + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = CalculatorWindow() + window.show() + + app.exec() \ No newline at end of file From 9233638637f9faf10e9c1e6a8a4d5f279e2758e2 Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra <133771854+NYSBC-Rudra@users.noreply.github.com> Date: Thu, 9 Nov 2023 13:16:04 -0500 Subject: [PATCH 66/79] adding gui folder to master --- gui/control_main.py | 5411 +++++++++++++++++++++++++++++++++ gui/data_loc_info.py | 113 + gui/dewar_tree.py | 481 +++ gui/dialog/__init__.py | 7 + gui/dialog/dewar.py | 92 + gui/dialog/puck_dialog.py | 93 + gui/dialog/raster_explore.py | 53 + gui/dialog/screen_defaults.py | 274 ++ gui/dialog/snap_comment.py | 40 + gui/dialog/staff_screen.py | 365 +++ gui/dialog/user_screen.py | 256 ++ gui/raster.py | 98 + 12 files changed, 7283 insertions(+) create mode 100644 gui/control_main.py create mode 100644 gui/data_loc_info.py create mode 100644 gui/dewar_tree.py create mode 100644 gui/dialog/__init__.py create mode 100644 gui/dialog/dewar.py create mode 100644 gui/dialog/puck_dialog.py create mode 100644 gui/dialog/raster_explore.py create mode 100644 gui/dialog/screen_defaults.py create mode 100644 gui/dialog/snap_comment.py create mode 100644 gui/dialog/staff_screen.py create mode 100644 gui/dialog/user_screen.py create mode 100644 gui/raster.py diff --git a/gui/control_main.py b/gui/control_main.py new file mode 100644 index 00000000..59dfd5cb --- /dev/null +++ b/gui/control_main.py @@ -0,0 +1,5411 @@ +import _thread +import functools +import logging +import math +import os +import sys +import time +import threading + +from queue import Queue +import cv2 +import numpy as np +from epics import PV +from PyMca5.PyMcaGui.physics.xrf.McaAdvancedFit import McaAdvancedFit +from PyMca5.PyMcaGui.pymca.McaWindow import McaWindow, ScanWindow +from PyMca5.PyMcaPhysics.xrf import Elements +from qt_epics.QtEpicsPVEntry import QtEpicsPVEntry +from qt_epics.QtEpicsPVLabel import QtEpicsPVLabel +from qtpy import QtCore, QtGui, QtWidgets +from qtpy.QtCore import QModelIndex, QRectF, Qt, QTimer +from qtpy.QtGui import QIntValidator +from qtpy.QtWidgets import QCheckBox, QFrame, QGraphicsPixmapItem, QApplication +from devices import GonioDevice, CameraDevice, MD2Device, LightDevice, MD2ApertureDevice + +import albulaUtils +import daq_utils +import db_lib +import lsdcOlog +from config_params import ( + CRYOSTREAM_ONLINE, + HUTCH_TIMER_DELAY, + SERVER_CHECK_DELAY, + RASTER_GUI_XREC_FILL_DELAY, + SAMPLE_TIMER_DELAY, + VALID_DET_DIST, + VALID_EXP_TIMES, + VALID_TOTAL_EXP_TIMES, + RasterStatus, + cryostreamTempPV, +) +from daq_utils import getBlConfig, setBlConfig +from element_info import element_info +from gui.data_loc_info import DataLocInfo +from gui.dewar_tree import DewarTree +from gui.dialog import ( + DewarDialog, + PuckDialog, + RasterExploreDialog, + ScreenDefaultsDialog, + SnapCommentDialog, + StaffScreenDialog, + UserScreenDialog, +) +from gui.raster import RasterCell, RasterGroup +from QPeriodicTable import QPeriodicTable +from threads import RaddoseThread, VideoThread, ServerCheckThread + +logger = logging.getLogger() +try: + import ispybLib +except Exception as e: + logger.error("lsdcGui: ISPYB import error, %s" % e) + + +def get_request_object_escan( + reqObj, + symbol, + runNum, + file_prefix, + base_path, + sampleName, + containerID, + samplePositionInContainer, + file_number_start, + exposure_time, + targetEnergy, + steps, + stepsize, +): + reqObj["element"] = symbol + reqObj["runNum"] = runNum + reqObj["file_prefix"] = str(file_prefix) + reqObj["basePath"] = str(base_path) + reqObj["directory"] = ( + str(base_path) + + "/" + + str(daq_utils.getVisitName()) + + "/" + + sampleName + + "/" + + str(runNum) + + "/" + + db_lib.getContainerNameByID(containerID) + + "_" + + str(samplePositionInContainer + 1) + + "/" + ) + try: + reqObj["file_number_start"] = int(file_number_start) + except ValueError as e: + logger.error("Problem with a value passed in - %s" % e) + reqObj["file_number_start"] = 1 + reqObj["exposure_time"] = float(exposure_time) + reqObj["protocol"] = "eScan" + reqObj["scanEnergy"] = targetEnergy + reqObj["runChooch"] = True # just hardcode for now + reqObj["steps"] = int(steps) + reqObj["stepsize"] = float(stepsize) + return reqObj + + +class ControlMain(QtWidgets.QMainWindow): + # 1/13/15 - are these necessary? + Signal = QtCore.Signal() + refreshTreeSignal = QtCore.Signal() + serverMessageSignal = QtCore.Signal(str) + serverPopupMessageSignal = QtCore.Signal(str) + programStateSignal = QtCore.Signal(str) + pauseButtonStateSignal = QtCore.Signal(str) + + xrecRasterSignal = QtCore.Signal(str) + choochResultSignal = QtCore.Signal(str) + energyChangeSignal = QtCore.Signal(float) + mountedPinSignal = QtCore.Signal(int) + beamSizeSignal = QtCore.Signal(float) + controlMasterSignal = QtCore.Signal(int) + zebraArmStateSignal = QtCore.Signal(int) + govRobotSeReachSignal = QtCore.Signal(int) + govRobotSaReachSignal = QtCore.Signal(int) + govRobotDaReachSignal = QtCore.Signal(int) + govRobotBlReachSignal = QtCore.Signal(int) + detMessageSignal = QtCore.Signal(str) + sampleFluxSignal = QtCore.Signal(float) + zebraPulseStateSignal = QtCore.Signal(int) + stillModeStateSignal = QtCore.Signal(int) + zebraDownloadStateSignal = QtCore.Signal(int) + zebraSentTriggerStateSignal = QtCore.Signal(int) + zebraReturnedTriggerStateSignal = QtCore.Signal(int) + fastShutterSignal = QtCore.Signal(float) + gripTempSignal = QtCore.Signal(float) + ringCurrentSignal = QtCore.Signal(float) + beamAvailableSignal = QtCore.Signal(float) + sampleExposedSignal = QtCore.Signal(float) + sampMoveSignal = QtCore.Signal(int, str) + roiChangeSignal = QtCore.Signal(int, str) + highMagCursorChangeSignal = QtCore.Signal(int, str) + lowMagCursorChangeSignal = QtCore.Signal(int, str) + cryostreamTempSignal = QtCore.Signal(str) + sampleZoomChangeSignal = QtCore.Signal(object) + + def __init__(self): + super(ControlMain, self).__init__() + self.SelectedItemData = "" # attempt to know what row is selected + self.popUpMessageInit = 1 # I hate these next two, but I don't want to catch old messages. Fix later, maybe. + self.textWindowMessageInit = 1 + self.processID = os.getpid() + self.popupMessage = QtWidgets.QErrorMessage(self) + self.popupMessage.setStyleSheet("background-color: red") + self.popupMessage.setModal(False) + self.groupName = "skinner" + self.scannerType = getBlConfig("scannerType") + self.vectorStart = None + self.vectorEnd = None + self.centerMarkerCharSize = 20 + self.centerMarkerCharOffsetX = 12 + self.centerMarkerCharOffsetY = 18 + self.currentRasterCellList = [] + self.redPen = QtGui.QPen(QtCore.Qt.red) + self.bluePen = QtGui.QPen(QtCore.Qt.blue) + self.yellowPen = QtGui.QPen(QtCore.Qt.yellow) + albulaUtils.startup_albula() + self.initUI() + self.initOphyd() + self.govStateMessagePV = PV(daq_utils.pvLookupDict["governorMessage"]) + self.zoom1FrameRatePV = PV(daq_utils.pvLookupDict["zoom1FrameRate"]) + self.zoom2FrameRatePV = PV(daq_utils.pvLookupDict["zoom2FrameRate"]) + self.zoom3FrameRatePV = PV(daq_utils.pvLookupDict["zoom3FrameRate"]) + self.zoom4FrameRatePV = PV(daq_utils.pvLookupDict["zoom4FrameRate"]) + self.sampleFluxPV = PV(daq_utils.pvLookupDict["sampleFlux"]) + self.beamFlux_pv = PV(daq_utils.pvLookupDict["flux"]) + self.stillMode_pv = PV(daq_utils.pvLookupDict["stillMode"]) + self.standardMode_pv = PV(daq_utils.pvLookupDict["standardMode"]) + self.lowMagCursorX_pv = PV(daq_utils.pvLookupDict["lowMagCursorX"]) + self.lowMagCursorY_pv = PV(daq_utils.pvLookupDict["lowMagCursorY"]) + self.highMagCursorX_pv = PV(daq_utils.pvLookupDict["highMagCursorX"]) + self.highMagCursorY_pv = PV(daq_utils.pvLookupDict["highMagCursorY"]) + self.fastShutterOpenPos_pv = PV(daq_utils.pvLookupDict["fastShutterOpenPos"]) + self.gripTemp_pv = PV(daq_utils.pvLookupDict["gripTemp"]) + if getBlConfig(CRYOSTREAM_ONLINE): + self.cryostreamTemp_pv = PV(cryostreamTempPV[daq_utils.beamline]) + if daq_utils.beamline == "fmx": + self.slit1XGapSP_pv = PV(daq_utils.motor_dict["slit1XGap"] + ".VAL") + self.slit1YGapSP_pv = PV(daq_utils.motor_dict["slit1YGap"] + ".VAL") + ringCurrentPvName = "SR:C03-BI{DCCT:1}I:Real-I" + self.ringCurrent_pv = PV(ringCurrentPvName) + + self.beamAvailable_pv = PV(daq_utils.pvLookupDict["beamAvailable"]) + self.sampleExposed_pv = PV(daq_utils.pvLookupDict["exposing"]) + + self.beamSize_pv = PV(daq_utils.beamlineComm + "size_mode") + self.energy_pv = PV(daq_utils.motor_dict["energy"] + ".RBV") + self.rasterStepDefs = {"Coarse": 20.0, "Fine": 10.0, "VFine": 5.0} + self.createSampleTab() + + self.initCallbacks() + if self.scannerType != "PI": + self.motPos = { + "x": self.gon.x.val(), + "y": self.gon.y.val(), + "z": self.gon.z.val(), + "omega": self.gon.omega.val(), + } + else: + self.motPos = { + "x": self.gon.x.val(), + "y": self.gon.y.val(), + "z": self.gon.z.val(), + "omega": self.gon.omega.val(), + "fineX": self.sampFineX_pv.get(), + "fineY": self.sampFineY_pv.get(), + "fineZ": self.sampFineZ_pv.get(), + } + self.staffScreenDialog = StaffScreenDialog(self, show=False) + if daq_utils.beamline == "nyx": # requires staffScreenDialog to be present + self.staffScreenDialog.fastDPCheckBox.setDisabled(True) + + self.dewarTree.refreshTreeDewarView() + if self.mountedPin_pv.get() == "": + mountedPin = db_lib.beamlineInfo(daq_utils.beamline, "mountedSample")[ + "sampleID" + ] + self.mountedPin_pv.put(mountedPin) + self.rasterExploreDialog = RasterExploreDialog() + self.userScreenDialog = UserScreenDialog(self) + self.detDistMotorEntry.getEntry().setText( + self.detDistRBVLabel.getEntry().text() + ) # this is to fix the current val being overwritten by reso + self.proposalID = -999999 + if len(sys.argv) > 1: + if sys.argv[1] == "master": + self.changeControlMasterCB(1) + self.controlMasterCheckBox.setChecked(True) + self.XRFInfoDict = self.parseXRFTable() # I don't like this + + def setGuiValues(self, values): + for item, value in values.items(): + logger.info("resetting %s to %s" % (item, value)) + if item == "osc_start": + self.osc_start_ledit.setText("%.3f" % float(value)) + elif item == "osc_end": + self.osc_end_ledit.setText("%.3f" % float(value)) + elif item == "osc_range": + self.osc_range_ledit.setText("%.3f" % float(value)) + elif item == "img_width": + self.img_width_ledit.setText("%.3f" % float(value)) + elif item == "exp_time": + self.exp_time_ledit.setText("%.3f" % float(value)) + elif item == "transmission": + self.transmission_ledit.setText("%.3f" % float(value)) + elif item == "resolution": + self.resolution_ledit.setText("%.2f" % float(value)) + else: + logger.error("setGuiValues unknown item: %s value: %s" % (item, value)) + + def parseXRFTable(self): + XRFFile = open(os.environ["CONFIGDIR"] + "/XRF-AMX_simple.txt") + XRFInfoDict = {} + for line in XRFFile.readlines(): + tokens = line.split() + XRFInfoDict[tokens[0]] = int(float(tokens[5]) * 100) + XRFFile.close() + return XRFInfoDict + + def closeEvent(self, evnt): + evnt.accept() + sys.exit() # doing this to close any windows left open + + def initVideo2(self, frequency): + #self.captureHighMag = cv2.VideoCapture(daq_utils.highMagCamURL) + logger.debug('highMagCamURL: "' + daq_utils.highMagCamURL + '"') + + def initVideo4(self, frequency): + #self.captureHighMagZoom = cv2.VideoCapture(daq_utils.highMagZoomCamURL) + logger.debug('highMagZoomCamURL: "' + daq_utils.highMagZoomCamURL + '"') + + def initVideo3(self, frequency): + #self.captureLowMagZoom = cv2.VideoCapture(daq_utils.lowMagZoomCamURL) + logger.debug('lowMagZoomCamURL: "' + daq_utils.lowMagZoomCamURL + '"') + + def createSampleTab(self): + sampleTab = QtWidgets.QWidget() + splitter1 = QtWidgets.QSplitter(Qt.Horizontal) + vBoxlayout = QtWidgets.QVBoxLayout() + self.dewarTreeFrame = QFrame() + vBoxDFlayout = QtWidgets.QVBoxLayout() + self.selectedSampleRequest = {} + self.selectedSampleID = "" + self.dewarTree = DewarTree(self) + self.dewarTree.clicked[QModelIndex].connect(self.row_clicked) + treeSelectBehavior = QtWidgets.QAbstractItemView.SelectItems + treeSelectMode = QtWidgets.QAbstractItemView.ExtendedSelection + self.dewarTree.setSelectionMode(treeSelectMode) + self.dewarTree.setSelectionBehavior(treeSelectBehavior) + hBoxRadioLayout1 = QtWidgets.QHBoxLayout() + self.viewRadioGroup = QtWidgets.QButtonGroup() + self.priorityViewRadio = QtWidgets.QRadioButton("PriorityView") + self.priorityViewRadio.toggled.connect( + functools.partial(self.dewarViewToggledCB, "priorityView") + ) + self.viewRadioGroup.addButton(self.priorityViewRadio) + self.dewarViewRadio = QtWidgets.QRadioButton("DewarView") + self.dewarViewRadio.setChecked(True) + self.dewarViewRadio.toggled.connect( + functools.partial(self.dewarViewToggledCB, "dewarView") + ) + hBoxRadioLayout1.addWidget(self.dewarViewRadio) + hBoxRadioLayout1.addWidget(self.priorityViewRadio) + self.viewRadioGroup.addButton(self.dewarViewRadio) + vBoxDFlayout.addLayout(hBoxRadioLayout1) + vBoxDFlayout.addWidget(self.dewarTree) + queueSelectedButton = QtWidgets.QPushButton("Queue All Selected") + queueSelectedButton.clicked.connect(self.dewarTree.queueAllSelectedCB) + deQueueSelectedButton = QtWidgets.QPushButton("deQueue All Selected") + deQueueSelectedButton.clicked.connect(self.dewarTree.deQueueAllSelectedCB) + runQueueButton = QtWidgets.QPushButton("Collect Queue") + runQueueButton.setStyleSheet("background-color: green") + runQueueButton.clicked.connect(self.collectQueueCB) + stopRunButton = QtWidgets.QPushButton("Stop Collection") + stopRunButton.setStyleSheet("background-color: red") + stopRunButton.clicked.connect(self.stopRunCB) # immediate stop everything + puckToDewarButton = QtWidgets.QPushButton("Puck to Dewar...") + mountSampleButton = QtWidgets.QPushButton("Mount Sample") + mountSampleButton.clicked.connect(self.mountSampleCB) + unmountSampleButton = QtWidgets.QPushButton("Unmount Sample") + unmountSampleButton.clicked.connect(self.unmountSampleCB) + puckToDewarButton.clicked.connect(self.puckToDewarCB) + removePuckButton = QtWidgets.QPushButton("Remove Puck...") + removePuckButton.clicked.connect(self.removePuckCB) + expandAllButton = QtWidgets.QPushButton("Expand All") + expandAllButton.clicked.connect(self.dewarTree.expandAllCB) + collapseAllButton = QtWidgets.QPushButton("Collapse All") + collapseAllButton.clicked.connect(self.dewarTree.collapseAllCB) + self.pauseQueueButton = QtWidgets.QPushButton("Pause") + self.pauseQueueButton.clicked.connect(self.stopQueueCB) + emptyQueueButton = QtWidgets.QPushButton("Empty Queue") + emptyQueueButton.clicked.connect( + functools.partial(self.dewarTree.deleteSelectedCB, 1) + ) + warmupButton = QtWidgets.QPushButton("Warmup Gripper") + warmupButton.clicked.connect(self.warmupGripperCB) + restartServerButton = QtWidgets.QPushButton("Restart Server") + restartServerButton.clicked.connect(self.restartServerCB) + self.openShutterButton = QtWidgets.QPushButton("Open Photon Shutter") + self.openShutterButton.clicked.connect(self.openPhotonShutterCB) + self.popUserScreen = QtWidgets.QPushButton("User Screen...") + self.popUserScreen.clicked.connect(self.popUserScreenCB) + self.closeShutterButton = QtWidgets.QPushButton("Close Photon Shutter") + self.closeShutterButton.clicked.connect(self.closePhotonShutterCB) + self.closeShutterButton.setStyleSheet("background-color: red") + self.parkRobotButton = QtWidgets.QPushButton("Park Robot") + self.parkRobotButton.clicked.connect(self.parkRobotCB) + hBoxTreeButtsLayout = QtWidgets.QHBoxLayout() + vBoxTreeButtsLayoutLeft = QtWidgets.QVBoxLayout() + vBoxTreeButtsLayoutRight = QtWidgets.QVBoxLayout() + vBoxTreeButtsLayoutLeft.addWidget(runQueueButton) + vBoxTreeButtsLayoutLeft.addWidget(mountSampleButton) + vBoxTreeButtsLayoutLeft.addWidget(self.pauseQueueButton) + vBoxTreeButtsLayoutLeft.addWidget(queueSelectedButton) + vBoxTreeButtsLayoutLeft.addWidget(self.popUserScreen) + vBoxTreeButtsLayoutLeft.addWidget(warmupButton) + vBoxTreeButtsLayoutRight.addWidget(self.closeShutterButton) + vBoxTreeButtsLayoutRight.addWidget(self.parkRobotButton) + vBoxTreeButtsLayoutRight.addWidget(unmountSampleButton) + vBoxTreeButtsLayoutRight.addWidget(deQueueSelectedButton) + vBoxTreeButtsLayoutRight.addWidget(emptyQueueButton) + #vBoxTreeButtsLayoutRight.addWidget(restartServerButton) + hBoxTreeButtsLayout.addLayout(vBoxTreeButtsLayoutLeft) + hBoxTreeButtsLayout.addLayout(vBoxTreeButtsLayoutRight) + vBoxDFlayout.addLayout(hBoxTreeButtsLayout) + self.dewarTreeFrame.setLayout(vBoxDFlayout) + splitter1.addWidget(self.dewarTreeFrame) + splitter11 = QtWidgets.QSplitter(Qt.Horizontal) + self.mainSetupFrame = QFrame() + self.mainSetupFrame.setFixedHeight(890) + vBoxMainSetup = QtWidgets.QVBoxLayout() + self.mainToolBox = QtWidgets.QToolBox() + self.mainToolBox.setMinimumWidth(750) + self.mainColFrame = QFrame() + vBoxMainColLayout = QtWidgets.QVBoxLayout() + colParamsGB = QtWidgets.QGroupBox() + colParamsGB.setTitle("Acquisition") + vBoxColParams1 = QtWidgets.QVBoxLayout() + hBoxColParams1 = QtWidgets.QHBoxLayout() + colStartLabel = QtWidgets.QLabel("Oscillation Start:") + colStartLabel.setFixedWidth(140) + colStartLabel.setAlignment(QtCore.Qt.AlignCenter) + self.osc_start_ledit = QtWidgets.QLineEdit() + self.osc_start_ledit.setFixedWidth(60) + self.osc_start_ledit.setValidator(QtGui.QDoubleValidator()) + self.colEndLabel = QtWidgets.QLabel("Oscillation Range:") + self.colEndLabel.setAlignment(QtCore.Qt.AlignCenter) + self.colEndLabel.setFixedWidth(140) + self.osc_end_ledit = QtWidgets.QLineEdit() + self.setGuiValues({"osc_end": "180.0"}) + self.osc_end_ledit.setFixedWidth(60) + self.osc_end_ledit.setValidator(QtGui.QDoubleValidator()) + self.osc_end_ledit.textChanged[str].connect( + functools.partial(self.totalExpChanged, "oscEnd") + ) + #hBoxColParams1.addWidget(colStartLabel) + #hBoxColParams1.addWidget(self.osc_start_ledit) + #hBoxColParams1.addWidget(self.colEndLabel) + #hBoxColParams1.addWidget(self.osc_end_ledit) + hBoxColParams2 = QtWidgets.QHBoxLayout() + colRangeLabel = QtWidgets.QLabel("Oscillation Width:") + colRangeLabel.setFixedWidth(140) + colRangeLabel.setAlignment(QtCore.Qt.AlignCenter) + self.osc_range_ledit = QtWidgets.QLineEdit() + self.osc_range_ledit.setFixedWidth(60) + self.osc_range_ledit.setValidator(QtGui.QDoubleValidator(0.001, 3600, 3)) + self.stillModeCheckBox = QCheckBox("Stills") + self.stillModeCheckBox.setEnabled(False) + if self.stillModeStatePV.get(): + self.stillModeCheckBox.setChecked(True) + self.setGuiValues({"osc_range": "0.0"}) + self.osc_range_ledit.setEnabled(False) + else: + self.stillModeCheckBox.setChecked(False) + self.osc_range_ledit.setEnabled(True) + colExptimeLabel = QtWidgets.QLabel("ExposureTime:") + self.stillModeCheckBox.clicked.connect(self.stillModeUserPushCB) + self.osc_range_ledit.textChanged[str].connect( + functools.partial(self.totalExpChanged, "oscRange") + ) + colExptimeLabel.setFixedWidth(140) + colExptimeLabel.setAlignment(QtCore.Qt.AlignCenter) + self.exp_time_ledit = QtWidgets.QLineEdit() + self.exp_time_ledit.setFixedWidth(60) + self.exp_time_ledit.textChanged[str].connect(self.totalExpChanged) + self.exp_time_ledit.setValidator( + QtGui.QDoubleValidator( + VALID_EXP_TIMES[daq_utils.beamline]["min"], + VALID_EXP_TIMES[daq_utils.beamline]["max"], + VALID_EXP_TIMES[daq_utils.beamline]["digits"], + ) + ) + self.exp_time_ledit.textChanged.connect(self.checkEntryState) + #hBoxColParams2.addWidget(colRangeLabel) + #hBoxColParams2.addWidget(self.osc_range_ledit) + + #hBoxColParams2.addWidget(colExptimeLabel) + #hBoxColParams2.addWidget(self.exp_time_ledit) + hBoxColParams25 = QtWidgets.QHBoxLayout() + #hBoxColParams25.addWidget(self.stillModeCheckBox) + totalExptimeLabel = QtWidgets.QLabel("Total Exposure Time (s):") + totalExptimeLabel.setFixedWidth(155) + totalExptimeLabel.setAlignment(QtCore.Qt.AlignCenter) + self.totalExptime_ledit = QtWidgets.QLineEdit() + self.totalExptime_ledit.setReadOnly(True) + self.totalExptime_ledit.setFrame(False) + self.totalExptime_ledit.setFixedWidth(60) + self.totalExptime_ledit.setValidator( + QtGui.QDoubleValidator( + VALID_TOTAL_EXP_TIMES[daq_utils.beamline]["min"], + VALID_TOTAL_EXP_TIMES[daq_utils.beamline]["max"], + VALID_TOTAL_EXP_TIMES[daq_utils.beamline]["digits"], + ) + ) + self.totalExptime_ledit.textChanged.connect(self.checkEntryState) + + sampleLifetimeLabel = QtWidgets.QLabel("Estimated Sample Lifetime (s): ") + if daq_utils.beamline == "amx": + self.sampleLifetimeReadback = QtEpicsPVLabel( + daq_utils.pvLookupDict["sampleLifetime"], self, 70, 2 + ) + self.sampleLifetimeReadback_ledit = self.sampleLifetimeReadback.getEntry() + else: + calcLifetimeButton = QtWidgets.QPushButton("Calc. Lifetime") + calcLifetimeButton.clicked.connect(self.calcLifetimeCB) + self.sampleLifetimeReadback_ledit = QtWidgets.QLabel() + self.calcLifetimeCB() + #hBoxColParams25.addWidget(totalExptimeLabel) + #hBoxColParams25.addWidget(self.totalExptime_ledit) + # if (daq_utils.beamline == "fmx"): + # hBoxColParams25.addWidget(calcLifetimeButton) + #hBoxColParams25.addWidget(sampleLifetimeLabel) + #hBoxColParams25.addWidget(self.sampleLifetimeReadback_ledit) + hBoxColParams22 = QtWidgets.QHBoxLayout() + if daq_utils.beamline in ("fmx", "nyx"): + if getBlConfig("attenType") == "RI": + self.transmissionReadback = QtEpicsPVLabel( + daq_utils.pvLookupDict["RI_Atten_SP"], self, 60, 3 + ) + self.transmissionSetPoint = QtEpicsPVEntry( + daq_utils.pvLookupDict["RI_Atten_SP"], self, 60, 3 + ) + colTransmissionLabel = QtWidgets.QLabel("Transmission (RI) (0.0-1.0):") + else: + self.transmissionReadback = QtEpicsPVLabel( + daq_utils.pvLookupDict["transmissionRBV"], self, 60, 3 + ) + self.transmissionSetPoint = QtEpicsPVEntry( + daq_utils.pvLookupDict["transmissionSet"], self, 60, 3 + ) + colTransmissionLabel = QtWidgets.QLabel("Transmission (BCU) (0.0-1.0):") + else: + self.transmissionReadback = QtEpicsPVLabel( + daq_utils.pvLookupDict["transmissionRBV"], self, 60, 3 + ) + self.transmissionSetPoint = QtEpicsPVEntry( + daq_utils.pvLookupDict["transmissionSet"], self, 60, 3 + ) + colTransmissionLabel = QtWidgets.QLabel("Transmission (0.0-1.0):") + self.transmissionReadback_ledit = self.transmissionReadback.getEntry() + + colTransmissionLabel.setAlignment(QtCore.Qt.AlignCenter) + colTransmissionLabel.setFixedWidth(190) + + transmisionSPLabel = QtWidgets.QLabel("SetPoint:") + + self.transmission_ledit = self.transmissionSetPoint.getEntry() + self.transmission_ledit.setValidator(QtGui.QDoubleValidator(0.001, 0.999, 3)) + self.setGuiValues({"transmission": getBlConfig("stdTrans")}) + self.transmission_ledit.returnPressed.connect(self.setTransCB) + if daq_utils.beamline == "fmx": + self.transmission_ledit.textChanged.connect(self.calcLifetimeCB) + setTransButton = QtWidgets.QPushButton("Set Trans") + setTransButton.clicked.connect(self.setTransCB) + beamsizeLabel = QtWidgets.QLabel("BeamSize:") + if daq_utils.beamline == "nyx": + # beamSizeOptionList = self.aperture.get_diameter_list() PV not working, needs investigation + beamSizeOptionList = ["10", "20", "30", "50", "100"] + current_index = self.aperture.current_index.get() + else: + beamSizeOptionList = ["V0H0", "V0H1", "V1H0", "V1H1"] + current_index = int(self.beamSize_pv.get()) + self.beamsizeComboBox = QtWidgets.QComboBox(self) + self.beamsizeComboBox.addItems(beamSizeOptionList) + self.beamsizeComboBox.setCurrentIndex(current_index) + self.beamsizeComboBox.activated[str].connect(self.beamsizeComboActivatedCB) + if daq_utils.beamline == "amx" or self.energy_pv.get() < 9000: + self.beamsizeComboBox.setEnabled(False) + hBoxColParams3 = QtWidgets.QHBoxLayout() + colEnergyLabel = QtWidgets.QLabel("Energy (eV):") + colEnergyLabel.setAlignment(QtCore.Qt.AlignCenter) + self.energyMotorEntry = QtEpicsPVLabel( + daq_utils.motor_dict["energy"] + ".RBV", self, 70, 2 + ) + self.energyReadback = self.energyMotorEntry.getEntry() + energySPLabel = QtWidgets.QLabel("SetPoint:") + self.energyMoveLedit = QtEpicsPVEntry( + daq_utils.motor_dict["energy"] + ".VAL", self, 75, 2 + ) + self.energy_ledit = self.energyMoveLedit.getEntry() + self.energy_ledit.setValidator(QtGui.QDoubleValidator()) + self.energy_ledit.returnPressed.connect(self.moveEnergyCB) + moveEnergyButton = QtWidgets.QPushButton("Move Energy") + moveEnergyButton.clicked.connect(self.moveEnergyCB) + #hBoxColParams3.addWidget(colEnergyLabel) + #hBoxColParams3.addWidget(self.energyReadback) + #hBoxColParams3.addWidget(energySPLabel) + #hBoxColParams3.addWidget(self.energy_ledit) + #hBoxColParams22.addWidget(colTransmissionLabel) + #hBoxColParams22.addWidget(self.transmissionReadback_ledit) + #hBoxColParams22.addWidget(transmisionSPLabel) + #hBoxColParams22.addWidget(self.transmission_ledit) + #hBoxColParams22.insertSpacing(5, 100) + #hBoxColParams22.addWidget(beamsizeLabel) + #hBoxColParams22.addWidget(self.beamsizeComboBox) + hBoxColParams4 = QtWidgets.QHBoxLayout() + colBeamWLabel = QtWidgets.QLabel("Beam Width:") + colBeamWLabel.setFixedWidth(140) + colBeamWLabel.setAlignment(QtCore.Qt.AlignCenter) + self.beamWidth_ledit = QtWidgets.QLineEdit() + self.beamWidth_ledit.setFixedWidth(60) + self.beamWidth_ledit.setText(getBlConfig("screen_default_beamWidth")) + colBeamHLabel = QtWidgets.QLabel("Beam Height:") + colBeamHLabel.setFixedWidth(140) + colBeamHLabel.setAlignment(QtCore.Qt.AlignCenter) + self.beamHeight_ledit = QtWidgets.QLineEdit() + self.beamHeight_ledit.setFixedWidth(60) + self.beamHeight_ledit.setText(getBlConfig("screen_default_beamHeight")) + hBoxColParams4.addWidget(colBeamWLabel) + hBoxColParams4.addWidget(self.beamWidth_ledit) + hBoxColParams4.addWidget(colBeamHLabel) + hBoxColParams4.addWidget(self.beamHeight_ledit) + hBoxColParams5 = QtWidgets.QHBoxLayout() + colResoLabel = QtWidgets.QLabel("Edge Resolution:") + colResoLabel.setAlignment(QtCore.Qt.AlignCenter) + self.resolution_ledit = QtWidgets.QLineEdit() + self.resolution_ledit.setFixedWidth(60) + self.resolution_ledit.setValidator(QtGui.QDoubleValidator()) + self.resolution_ledit.textEdited[str].connect(self.resoTextChanged) + if daq_utils.beamline == "nyx": + self.resolution_ledit.setEnabled(False) + detDistLabel = QtWidgets.QLabel("Detector Dist.") + #detDistLabel.setAlignment(QtCore.Qt.AlignCenter) + detDistRBLabel = QtWidgets.QLabel("Readback:") + self.detDistRBVLabel = QtEpicsPVLabel( + daq_utils.motor_dict["detectorDist"] + ".RBV", self, 70 + ) + detDistSPLabel = QtWidgets.QLabel("SetPoint:") + self.detDistMotorEntry = QtEpicsPVEntry( + daq_utils.motor_dict["detectorDist"] + ".VAL", self, 70, 2 + ) + self.detDistMotorEntry.getEntry().setValidator( + QtGui.QDoubleValidator( + VALID_DET_DIST[daq_utils.beamline]["min"], + VALID_DET_DIST[daq_utils.beamline]["max"], + VALID_DET_DIST[daq_utils.beamline]["digits"], + ) + ) + self.detDistMotorEntry.getEntry().textChanged[str].connect( + self.detDistTextChanged + ) + self.detDistMotorEntry.getEntry().textChanged[str].connect(self.checkEntryState) + self.detDistMotorEntry.getEntry().returnPressed.connect(self.moveDetDistCB) + self.moveDetDistButton = QtWidgets.QPushButton("Move Detector") + self.moveDetDistButton.clicked.connect(self.moveDetDistCB) + #hBoxColParams3.addWidget(detDistLabel) + #hBoxColParams3.addWidget(self.detDistRBVLabel.getEntry()) + #hBoxColParams3.addWidget(detDistSPLabel) + #hBoxColParams3.addWidget(self.detDistMotorEntry.getEntry()) + hBoxColParams6 = QtWidgets.QHBoxLayout() + hBoxColParams6.setAlignment(QtCore.Qt.AlignLeft) + hBoxColParams7 = QtWidgets.QHBoxLayout() + hBoxColParams7.setAlignment(QtCore.Qt.AlignLeft) + centeringLabel = QtWidgets.QLabel("Sample Centering:") + centeringLabel.setFixedWidth(140) + centeringOptionList = ["Interactive", "AutoLoop", "AutoRaster", "Testing"] + self.centeringComboBox = QtWidgets.QComboBox(self) + self.centeringComboBox.addItems(centeringOptionList) + protoLabel = QtWidgets.QLabel("Protocol:") + font = QtGui.QFont() + font.setBold(True) + protoLabel.setFont(font) + protoLabel.setAlignment(QtCore.Qt.AlignCenter) + self.protoRadioGroup = QtWidgets.QButtonGroup() + self.protoStandardRadio = QtWidgets.QRadioButton("standard") + self.protoStandardRadio.setChecked(True) + self.protoStandardRadio.toggled.connect( + functools.partial(self.protoRadioToggledCB, "standard") + ) + self.protoStandardRadio.pressed.connect( + functools.partial(self.protoRadioToggledCB, "standard") + ) + self.protoRadioGroup.addButton(self.protoStandardRadio) + self.protoRasterRadio = QtWidgets.QRadioButton("raster") + self.protoRasterRadio.toggled.connect( + functools.partial(self.protoRadioToggledCB, "raster") + ) + self.protoRasterRadio.pressed.connect( + functools.partial(self.protoRadioToggledCB, "raster") + ) + self.protoRadioGroup.addButton(self.protoRasterRadio) + self.protoVectorRadio = QtWidgets.QRadioButton("vector") + self.protoRasterRadio.toggled.connect( + functools.partial(self.protoRadioToggledCB, "vector") + ) + self.protoRasterRadio.pressed.connect( + functools.partial(self.protoRadioToggledCB, "vector") + ) + self.protoRadioGroup.addButton(self.protoVectorRadio) + self.protoOtherRadio = QtWidgets.QRadioButton("other") + self.protoOtherRadio.setEnabled(False) + self.protoRadioGroup.addButton(self.protoOtherRadio) + if daq_utils.beamline == "nyx": + protoOptionList = ["standard","raster","vector"] # these should probably come from db + + else: + protoOptionList = [ + "standard", + "raster", + "vector", + "burn", + "rasterScreen", + "stepRaster", + "stepVector", + "multiCol", + "characterize", + "ednaCol", + ] # these should probably come from db + self.protoComboBox = QtWidgets.QComboBox(self) + self.protoComboBox.addItems(protoOptionList) + self.protoComboBox.activated[str].connect(self.protoComboActivatedCB) + hBoxColParams6.addWidget(protoLabel) + hBoxColParams6.addWidget(self.protoStandardRadio) + hBoxColParams6.addWidget(self.protoRasterRadio) + hBoxColParams6.addWidget(self.protoVectorRadio) + hBoxColParams6.addWidget(self.protoComboBox) + hBoxColParams7.addWidget(centeringLabel) + hBoxColParams7.addWidget(self.centeringComboBox) + #hBoxColParams7.addWidget(colResoLabel) + #hBoxColParams7.addWidget(self.resolution_ledit) + self.processingOptionsFrame = QFrame() + self.hBoxProcessingLayout1 = QtWidgets.QHBoxLayout() + self.hBoxProcessingLayout1.setAlignment(QtCore.Qt.AlignLeft) + procOptionLabel = QtWidgets.QLabel("Processing Options:") + procOptionLabel.setFixedWidth(200) + self.autoProcessingCheckBox = QCheckBox("AutoProcessing On") + self.autoProcessingCheckBox.setChecked(True) + self.autoProcessingCheckBox.stateChanged.connect(self.autoProcessingCheckCB) + self.fastEPCheckBox = QCheckBox("FastEP") + self.fastEPCheckBox.setChecked(False) + self.fastEPCheckBox.setEnabled(False) + self.dimpleCheckBox = QCheckBox("Dimple") + self.dimpleCheckBox.setChecked(True) + self.xia2CheckBox = QCheckBox("Xia2") + self.xia2CheckBox.setChecked(False) + self.hBoxProcessingLayout1.addWidget(self.autoProcessingCheckBox) + self.hBoxProcessingLayout1.addWidget(self.fastEPCheckBox) + self.hBoxProcessingLayout1.addWidget(self.dimpleCheckBox) + self.processingOptionsFrame.setLayout(self.hBoxProcessingLayout1) + self.rasterParamsFrame = QFrame() + self.vBoxRasterParams = QtWidgets.QVBoxLayout() + self.hBoxRasterLayout1 = QtWidgets.QHBoxLayout() + self.hBoxRasterLayout1.setAlignment(QtCore.Qt.AlignLeft) + self.hBoxRasterLayout2 = QtWidgets.QHBoxLayout() + self.hBoxRasterLayout2.setAlignment(QtCore.Qt.AlignLeft) + rasterStepLabel = QtWidgets.QLabel("Raster Step") + rasterStepLabel.setFixedWidth(110) + self.rasterStepEdit = QtWidgets.QLineEdit(str(self.rasterStepDefs["Coarse"])) + self.rasterStepEdit.textChanged[str].connect(self.rasterStepChanged) + self.rasterStepEdit.setFixedWidth(60) + self.rasterGrainRadioGroup = QtWidgets.QButtonGroup() + self.rasterGrainCoarseRadio = QtWidgets.QRadioButton("Coarse") + self.rasterGrainCoarseRadio.setChecked(False) + self.rasterGrainCoarseRadio.toggled.connect( + functools.partial(self.rasterGrainToggledCB, "Coarse") + ) + self.rasterGrainRadioGroup.addButton(self.rasterGrainCoarseRadio) + self.rasterGrainFineRadio = QtWidgets.QRadioButton("Fine") + self.rasterGrainFineRadio.setChecked(False) + self.rasterGrainFineRadio.toggled.connect( + functools.partial(self.rasterGrainToggledCB, "Fine") + ) + self.rasterGrainRadioGroup.addButton(self.rasterGrainFineRadio) + self.rasterGrainVFineRadio = QtWidgets.QRadioButton("VFine") + self.rasterGrainVFineRadio.setChecked(False) + self.rasterGrainVFineRadio.toggled.connect( + functools.partial(self.rasterGrainToggledCB, "VFine") + ) + self.rasterGrainRadioGroup.addButton(self.rasterGrainVFineRadio) + self.rasterGrainCustomRadio = QtWidgets.QRadioButton("Custom") + self.rasterGrainCustomRadio.setChecked(True) + self.rasterGrainCustomRadio.toggled.connect( + functools.partial(self.rasterGrainToggledCB, "Custom") + ) + self.rasterGrainRadioGroup.addButton(self.rasterGrainCustomRadio) + rasterEvalLabel = QtWidgets.QLabel("Raster\nEvaluate By:") + rasterEvalOptionList = ["Spot Count", "Resolution", "Intensity"] + self.rasterEvalComboBox = QtWidgets.QComboBox(self) + self.rasterEvalComboBox.addItems(rasterEvalOptionList) + self.rasterEvalComboBox.setCurrentIndex( + db_lib.beamlineInfo(daq_utils.beamline, "rasterScoreFlag")["index"] + ) + self.rasterEvalComboBox.activated[str].connect(self.rasterEvalComboActivatedCB) + self.hBoxRasterLayout1.addWidget(rasterStepLabel) + self.hBoxRasterLayout1.addWidget(self.rasterStepEdit) + self.hBoxRasterLayout1.addWidget(self.rasterGrainCoarseRadio) + self.hBoxRasterLayout1.addWidget(self.rasterGrainFineRadio) + self.hBoxRasterLayout1.addWidget(self.rasterGrainVFineRadio) + self.hBoxRasterLayout1.addWidget(self.rasterGrainCustomRadio) + self.hBoxRasterLayout1.addWidget(rasterEvalLabel) + self.hBoxRasterLayout1.addWidget(self.rasterEvalComboBox) + self.vBoxRasterParams.addLayout(self.hBoxRasterLayout1) + self.vBoxRasterParams.addLayout(self.hBoxRasterLayout2) + self.rasterParamsFrame.setLayout(self.vBoxRasterParams) + self.multiColParamsFrame = ( + QFrame() + ) # something for criteria to decide on which hotspots to collect on for multi-xtal + self.hBoxMultiColParamsLayout1 = QtWidgets.QHBoxLayout() + self.hBoxMultiColParamsLayout1.setAlignment(QtCore.Qt.AlignLeft) + multiColCutoffLabel = QtWidgets.QLabel("Diffraction Cutoff") + multiColCutoffLabel.setFixedWidth(110) + self.multiColCutoffEdit = QtWidgets.QLineEdit( + "320" + ) # may need to store this in DB at some point, it's a silly number for now + self.multiColCutoffEdit.setFixedWidth(60) + self.hBoxMultiColParamsLayout1.addWidget(multiColCutoffLabel) + self.hBoxMultiColParamsLayout1.addWidget(self.multiColCutoffEdit) + self.multiColParamsFrame.setLayout(self.hBoxMultiColParamsLayout1) + self.characterizeParamsFrame = QFrame() + vBoxCharacterizeParams1 = QtWidgets.QVBoxLayout() + self.hBoxCharacterizeLayout1 = QtWidgets.QHBoxLayout() + self.characterizeTargetLabel = QtWidgets.QLabel("Characterization Targets") + characterizeResoLabel = QtWidgets.QLabel("Resolution") + characterizeResoLabel.setAlignment(QtCore.Qt.AlignCenter) + self.characterizeResoEdit = QtWidgets.QLineEdit("3.0") + characterizeISIGLabel = QtWidgets.QLabel("I/Sigma") + characterizeISIGLabel.setAlignment(QtCore.Qt.AlignCenter) + self.characterizeISIGEdit = QtWidgets.QLineEdit("2.0") + self.characterizeAnomCheckBox = QCheckBox("Anomolous") + self.characterizeAnomCheckBox.setChecked(False) + self.hBoxCharacterizeLayout2 = QtWidgets.QHBoxLayout() + characterizeCompletenessLabel = QtWidgets.QLabel("Completeness") + characterizeCompletenessLabel.setAlignment(QtCore.Qt.AlignCenter) + self.characterizeCompletenessEdit = QtWidgets.QLineEdit("0.99") + characterizeMultiplicityLabel = QtWidgets.QLabel("Multiplicity") + characterizeMultiplicityLabel.setAlignment(QtCore.Qt.AlignCenter) + self.characterizeMultiplicityEdit = QtWidgets.QLineEdit("auto") + characterizeDoseLimitLabel = QtWidgets.QLabel("Dose Limit") + characterizeDoseLimitLabel.setAlignment(QtCore.Qt.AlignCenter) + self.characterizeDoseLimitEdit = QtWidgets.QLineEdit("100") + characterizeSpaceGroupLabel = QtWidgets.QLabel("Space Group") + characterizeSpaceGroupLabel.setAlignment(QtCore.Qt.AlignCenter) + self.characterizeSpaceGroupEdit = QtWidgets.QLineEdit("P1") + self.hBoxCharacterizeLayout1.addWidget(characterizeResoLabel) + self.hBoxCharacterizeLayout1.addWidget(self.characterizeResoEdit) + self.hBoxCharacterizeLayout1.addWidget(characterizeISIGLabel) + self.hBoxCharacterizeLayout1.addWidget(self.characterizeISIGEdit) + self.hBoxCharacterizeLayout1.addWidget(characterizeSpaceGroupLabel) + self.hBoxCharacterizeLayout1.addWidget(self.characterizeSpaceGroupEdit) + self.hBoxCharacterizeLayout1.addWidget(self.characterizeAnomCheckBox) + self.hBoxCharacterizeLayout2.addWidget(characterizeCompletenessLabel) + self.hBoxCharacterizeLayout2.addWidget(self.characterizeCompletenessEdit) + self.hBoxCharacterizeLayout2.addWidget(characterizeMultiplicityLabel) + self.hBoxCharacterizeLayout2.addWidget(self.characterizeMultiplicityEdit) + self.hBoxCharacterizeLayout2.addWidget(characterizeDoseLimitLabel) + self.hBoxCharacterizeLayout2.addWidget(self.characterizeDoseLimitEdit) + vBoxCharacterizeParams1.addWidget(self.characterizeTargetLabel) + vBoxCharacterizeParams1.addLayout(self.hBoxCharacterizeLayout1) + vBoxCharacterizeParams1.addLayout(self.hBoxCharacterizeLayout2) + self.characterizeParamsFrame.setLayout(vBoxCharacterizeParams1) + self.vectorParamsFrame = QFrame() + hBoxVectorLayout1 = QtWidgets.QHBoxLayout() + setVectorStartButton = QtWidgets.QPushButton("Vector\nStart") + setVectorStartButton.setStyleSheet("background-color: blue") + setVectorStartButton.clicked.connect( + lambda: self.setVectorPointCB("vectorStart") + ) + setVectorEndButton = QtWidgets.QPushButton("Vector\nEnd") + setVectorEndButton.setStyleSheet("background-color: red") + setVectorEndButton.clicked.connect(lambda: self.setVectorPointCB("vectorEnd")) + self.vecLine = None + vectorFPPLabel = QtWidgets.QLabel("Number of Wedges") + self.vectorFPP_ledit = QtWidgets.QLineEdit("1") + self.vectorFPP_ledit.setValidator(QIntValidator(self)) + vecLenLabel = QtWidgets.QLabel(" Length(microns):") + self.vecLenLabelOutput = QtWidgets.QLabel("---") + vecSpeedLabel = QtWidgets.QLabel(" Speed(microns/s):") + self.vecSpeedLabelOutput = QtWidgets.QLabel("---") + hBoxVectorLayout1.addWidget(setVectorStartButton) + hBoxVectorLayout1.addWidget(setVectorEndButton) + hBoxVectorLayout1.addWidget(vectorFPPLabel) + hBoxVectorLayout1.addWidget(self.vectorFPP_ledit) + hBoxVectorLayout1.addWidget(vecLenLabel) + hBoxVectorLayout1.addWidget(self.vecLenLabelOutput) + hBoxVectorLayout1.addWidget(vecSpeedLabel) + hBoxVectorLayout1.addWidget(self.vecSpeedLabelOutput) + self.vectorParamsFrame.setLayout(hBoxVectorLayout1) + vBoxColParams1.addLayout(hBoxColParams1) + vBoxColParams1.addLayout(hBoxColParams2) + vBoxColParams1.addLayout(hBoxColParams25) + vBoxColParams1.addLayout(hBoxColParams22) + vBoxColParams1.addLayout(hBoxColParams3) + #vBoxColParams1.addLayout(hBoxColParams7) + #vBoxColParams1.addLayout(hBoxColParams6) + #vBoxColParams1.addWidget(self.rasterParamsFrame) + #vBoxColParams1.addWidget(self.multiColParamsFrame) + #vBoxColParams1.addWidget(self.vectorParamsFrame) + #vBoxColParams1.addWidget(self.characterizeParamsFrame) + #vBoxColParams1.addWidget(self.processingOptionsFrame) + mikesGB = QtWidgets.QGroupBox() + mikesGB.setTitle("Acquisition") + + paramSubspace = QtWidgets.QGridLayout() + + #paramSubspace.setColumnMinimumWidth(0, 140) + #paramSubspace.setColumnMinimumWidth(1, 90) + #paramSubspace.setColumnMinimumWidth(2, 140) + #paramSubspace.setColumnMinimumWidth(3, 90) + #paramSubspace.setColumnMinimumWidth(4, 90) + #paramSubspace.setColumnStretch(1) learn about stretch factor and then update + + + # Parameter Collection Column 1, Labels + colStartLabel.setAlignment(QtCore.Qt.AlignLeft) + paramSubspace.addWidget(colStartLabel,1,0, alignment=QtCore.Qt.AlignLeft) + self.colEndLabel.setAlignment(QtCore.Qt.AlignLeft) + paramSubspace.addWidget(self.colEndLabel,2,0, alignment=QtCore.Qt.AlignLeft) + colRangeLabel.setAlignment(QtCore.Qt.AlignLeft) + paramSubspace.addWidget(colRangeLabel,0,0, alignment=QtCore.Qt.AlignLeft) + colExptimeLabel.setAlignment(QtCore.Qt.AlignLeft) + paramSubspace.addWidget(colExptimeLabel,3,0, alignment=QtCore.Qt.AlignLeft) + totalExptimeLabel.setAlignment(QtCore.Qt.AlignLeft) + paramSubspace.addWidget(totalExptimeLabel,4,0, alignment=QtCore.Qt.AlignLeft) + # Parameter Collection Column 2, Input Boxes + paramSubspace.addWidget(self.osc_start_ledit,1,1, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(self.osc_end_ledit,2,1, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(self.osc_range_ledit,0,1, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(self.exp_time_ledit,3,1, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(self.totalExptime_ledit,4,1, alignment=QtCore.Qt.AlignLeft) + # Parameter Collection Column 3, Labels + paramSubspace.addWidget(detDistLabel,0,2, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(colResoLabel,1,2, alignment=QtCore.Qt.AlignLeft) + #paramSubspace.addWidget(detDistSPLabel,1,2, alignment=QtCore.Qt.AlignLeft) + #hBoxColParams7.addWidget(colResoLabel) + paramSubspace.addWidget(colEnergyLabel,2,2, alignment=QtCore.Qt.AlignLeft) + #paramSubspace.addWidget(energySPLabel,3,2, alignment=QtCore.Qt.AlignLeft) + colTransmissionLabel.setAlignment(QtCore.Qt.AlignLeft) + paramSubspace.addWidget(colTransmissionLabel,3,2, alignment=QtCore.Qt.AlignLeft) + transmisionSPLabel.setAlignment(QtCore.Qt.AlignLeft) + #paramSubspace.addWidget(transmisionSPLabel,3,2, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(beamsizeLabel,4,2, alignment=QtCore.Qt.AlignLeft) + # Parameter Collection Column 4, Input Boxes + paramSubspace.addWidget(self.detDistMotorEntry.getEntry(),0,3, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(self.resolution_ledit,1,3, alignment=QtCore.Qt.AlignLeft) + #hBoxColParams7.addWidget(self.resolution_ledit) + paramSubspace.addWidget(self.energy_ledit,2,3, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(self.transmission_ledit,3,3, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(self.beamsizeComboBox,4,3, alignment=QtCore.Qt.AlignLeft) + # Param Collection Column 5, RBV + paramSubspace.addWidget(self.energyReadback,2,4, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(self.detDistRBVLabel.getEntry(),0,4, alignment=QtCore.Qt.AlignLeft) + paramSubspace.addWidget(self.transmissionReadback_ledit,3,4, alignment=QtCore.Qt.AlignLeft) + + improvedParamSpacing = QtWidgets.QVBoxLayout() + improvedParamSpacing.addWidget(self.stillModeCheckBox) + improvedParamSpacing.addLayout(paramSubspace) + improvedParamSpacing.addLayout(hBoxColParams7) + improvedParamSpacing.addLayout(hBoxColParams6) + improvedParamSpacing.addWidget(self.rasterParamsFrame) + improvedParamSpacing.addWidget(self.multiColParamsFrame) + improvedParamSpacing.addWidget(self.vectorParamsFrame) + improvedParamSpacing.addWidget(self.characterizeParamsFrame) + improvedParamSpacing.addWidget(self.processingOptionsFrame) + mikesGB.setLayout(improvedParamSpacing) + + self.rasterParamsFrame.hide() + self.multiColParamsFrame.hide() + self.characterizeParamsFrame.hide() + colParamsGB.setLayout(vBoxColParams1) + self.dataPathGB = DataLocInfo(self) + hBoxDisplayOptionLayout= QtWidgets.QHBoxLayout() + self.albulaDispCheckBox = QCheckBox("Display Data (Albula)") + self.albulaDispCheckBox.setChecked(False) + hBoxDisplayOptionLayout.addWidget(self.albulaDispCheckBox) + #vBoxMainColLayout.addWidget(colParamsGB) + vBoxMainColLayout.addWidget(mikesGB) + + vBoxMainColLayout.addWidget(self.dataPathGB) + self.mainColFrame.setLayout(vBoxMainColLayout) + self.mainToolBox.addItem(self.mainColFrame, "Collection Parameters") + editSampleButton = QtWidgets.QPushButton("Apply Changes") + editSampleButton.clicked.connect(self.editSelectedRequestsCB) + cloneRequestButton = QtWidgets.QPushButton("Clone Raster Request") + cloneRequestButton.clicked.connect(self.cloneRequestCB) + hBoxPriorityLayout1 = QtWidgets.QHBoxLayout() + priorityEditLabel = QtWidgets.QLabel("Priority Edit") + priorityTopButton = QtWidgets.QPushButton(" >> ") + priorityUpButton = QtWidgets.QPushButton(" > ") + priorityDownButton = QtWidgets.QPushButton(" < ") + priorityBottomButton = QtWidgets.QPushButton(" << ") + priorityTopButton.clicked.connect(self.topPriorityCB) + priorityBottomButton.clicked.connect(self.bottomPriorityCB) + priorityUpButton.clicked.connect(self.upPriorityCB) + priorityDownButton.clicked.connect(self.downPriorityCB) + hBoxPriorityLayout1.addWidget(priorityEditLabel) + hBoxPriorityLayout1.addWidget(priorityBottomButton) + hBoxPriorityLayout1.addWidget(priorityDownButton) + hBoxPriorityLayout1.addWidget(priorityUpButton) + hBoxPriorityLayout1.addWidget(priorityTopButton) + queueSampleButton = QtWidgets.QPushButton("Add Requests to Queue") + queueSampleButton.clicked.connect(self.addRequestsToAllSelectedCB) + deleteSampleButton = QtWidgets.QPushButton("Delete Requests") + deleteSampleButton.clicked.connect( + functools.partial(self.dewarTree.deleteSelectedCB, 0) + ) + editScreenParamsButton = QtWidgets.QPushButton("Edit Raster Params...") + editScreenParamsButton.clicked.connect(self.editScreenParamsCB) + vBoxMainSetup.addWidget(self.mainToolBox) + vBoxMainSetup.addLayout(hBoxPriorityLayout1) + vBoxMainSetup.addWidget(queueSampleButton) + vBoxMainSetup.addWidget(editSampleButton) + vBoxMainSetup.addWidget(cloneRequestButton) + + vBoxMainSetup.addWidget(editScreenParamsButton) + self.mainSetupFrame.setLayout(vBoxMainSetup) + self.VidFrame = QFrame() + self.VidFrame.setFixedWidth(680) + vBoxVidLayout = QtWidgets.QVBoxLayout() + self.captureLowMag = None + self.captureHighMag = None + self.captureHighMagZoom = None + self.captureLowMagZoom = None + if daq_utils.has_xtalview: + if self.zoom3FrameRatePV.get() != 0: + _thread.start_new_thread(self.initVideo2, (0.25,)) # highMag + if self.zoom4FrameRatePV.get() != 0: + _thread.start_new_thread( + self.initVideo4, (0.25,) + ) # this sets up highMagDigiZoom + if self.zoom2FrameRatePV.get() != 0: + _thread.start_new_thread( + self.initVideo3, (0.25,) + ) # this sets up lowMagDigiZoom + if self.zoom1FrameRatePV.get() != 0: + #self.captureLowMag = cv2.VideoCapture(daq_utils.lowMagCamURL) + logger.debug('lowMagCamURL: "' + daq_utils.lowMagCamURL + '"') + + #self.captureLowMag = cv2.VideoCapture(daq_utils.lowMagCamURL) + self.capture = self.captureLowMag + #self.frame_queue = Queue() + #self.active_camera_threads = [] + self.timerSample = QTimer() + #self.timerSample.timeout.connect(self.sampleFrameCB) + #self.timerSample.timeout.connect(self.timerSampleRefresh) + #self.timerSample.start(SAMPLE_TIMER_DELAY) + + self.centeringMarksList = [] + self.rasterList = [] + self.rasterDefList = [] + self.polyPointItems = [] + self.rasterPoly = None + self.measureLine = None + self.scene = QtWidgets.QGraphicsScene(0, 0, 640, 512, self) + hBoxHutchVidsLayout = QtWidgets.QHBoxLayout() + self.sceneHutchCorner = QtWidgets.QGraphicsScene(0, 0, 320, 180, self) + self.sceneHutchTop = QtWidgets.QGraphicsScene(0, 0, 320, 180, self) + self.scene.keyPressEvent = self.sceneKey + self.view = QtWidgets.QGraphicsView(self.scene) + self.viewHutchCorner = QtWidgets.QGraphicsView(self.sceneHutchCorner) + self.viewHutchTop = QtWidgets.QGraphicsView(self.sceneHutchTop) + self.pixmap_item = QtWidgets.QGraphicsPixmapItem(None) + self.scene.addItem(self.pixmap_item) + self.pixmap_item_HutchCorner = QtWidgets.QGraphicsPixmapItem(None) + self.sceneHutchCorner.addItem(self.pixmap_item_HutchCorner) + self.pixmap_item_HutchTop = QtWidgets.QGraphicsPixmapItem(None) + self.sceneHutchTop.addItem(self.pixmap_item_HutchTop) + + self.pixmap_item.mousePressEvent = self.pixelSelect + try: + if QtGui.QColor.isValidColor(getBlConfig("defaultOverlayColor")): + centerMarkBrush = QtGui.QBrush( + QtGui.QColor(getBlConfig("defaultOverlayColor")) + ) + else: + centerMarkBrush = QtGui.QBrush(QtCore.Qt.blue) + except KeyError as e: + logger.warning("No value found for defaultOverlayColor") + centerMarkBrush = QtGui.QBrush(QtCore.Qt.blue) + centerMarkPen = QtGui.QPen(centerMarkBrush, 2.0) + self.centerMarker = QtWidgets.QGraphicsSimpleTextItem("+") + self.centerMarker.setZValue(10.0) + self.centerMarker.setBrush(centerMarkBrush) + font = QtGui.QFont("DejaVu Sans Light", self.centerMarkerCharSize, weight=0) + self.centerMarker.setFont(font) + self.scene.addItem(self.centerMarker) + self.centerMarker.setPos( + self.getMD2BeamCenterX() - self.centerMarkerCharOffsetX, + self.getMD2BeamCenterY() - self.centerMarkerCharOffsetY, + ) + self.zoomRadioGroup = QtWidgets.QButtonGroup() + self.zoom1Radio = QtWidgets.QRadioButton("Mag1") + self.zoom1Radio.setChecked(True) + self.zoom1Radio.toggled.connect( + functools.partial(self.zoomLevelToggledCB, "Zoom1") + ) + self.zoomRadioGroup.addButton(self.zoom1Radio) + self.zoom2Radio = QtWidgets.QRadioButton("Mag2") + self.zoom2Radio.toggled.connect( + functools.partial(self.zoomLevelToggledCB, "Zoom2") + ) + self.zoom3Radio = QtWidgets.QRadioButton("Mag3") + self.zoom3Radio.toggled.connect( + functools.partial(self.zoomLevelToggledCB, "Zoom3") + ) + self.zoom4Radio = QtWidgets.QRadioButton("Mag4") + self.zoom4Radio.toggled.connect( + functools.partial(self.zoomLevelToggledCB, "Zoom4") + ) + if daq_utils.sampleCameraCount >= 2: + self.zoomRadioGroup.addButton(self.zoom2Radio) + if daq_utils.sampleCameraCount >= 3: + self.zoomRadioGroup.addButton(self.zoom3Radio) + if daq_utils.sampleCameraCount >= 4: + self.zoomRadioGroup.addButton(self.zoom4Radio) + else: + self.zoomLevelComboBox = QtWidgets.QComboBox(self) + self.zoomLevelComboBox.addItems(["1","2","3","4","5","6","7"]) + self.zoomLevelComboBox.activated[str].connect(self.zoomLevelComboActivatedCB) + self.zoom1Radio.hide() + self.zoom2Radio.hide() + self.zoom3Radio.hide() + self.zoom4Radio.hide() + hBoxZoomLevelLayout = QtWidgets.QHBoxLayout() + hBoxZoomLevelLayout.addWidget(self.zoomLevelComboBox) + + + beamOverlayPen = QtGui.QPen(QtCore.Qt.red) + self.tempBeamSizeXMicrons = 30 + self.tempBeamSizeYMicrons = 30 + self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) + self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) + self.overlayPosOffsetX = self.centerMarkerCharOffsetX - 1 + self.overlayPosOffsetY = self.centerMarkerCharOffsetY - 1 + self.beamSizeOverlay = QtWidgets.QGraphicsRectItem( + self.centerMarker.x() - self.overlayPosOffsetX, + self.centerMarker.y() - self.overlayPosOffsetY, + self.beamSizeXPixels, + self.beamSizeYPixels, + ) + self.beamSizeOverlay.setPen(beamOverlayPen) + self.scene.addItem(self.beamSizeOverlay) + self.beamSizeOverlay.setVisible(False) + self.beamSizeOverlay.setRect( + self.overlayPosOffsetX + self.centerMarker.x() - (self.beamSizeXPixels / 2), + self.overlayPosOffsetY + self.centerMarker.y() - (self.beamSizeYPixels / 2), + self.beamSizeXPixels, + self.beamSizeYPixels, + ) + try: + if QtGui.QColor.isValidColor(getBlConfig("defaultOverlayColor")): + scaleBrush = QtGui.QBrush( + QtGui.QColor(getBlConfig("defaultOverlayColor")) + ) + else: + scaleBrush = QtGui.QBrush(QtCore.Qt.blue) + except KeyError as e: + logger.warning("No value found for defaultOverlayColor") + scaleBrush = QtGui.QBrush(QtCore.Qt.blue) + scalePen = QtGui.QPen(scaleBrush, 2.0) + scaleTextPen = QtGui.QPen(scaleBrush, 1.0) + self.imageScaleLineLen = 50 + self.imageScale = self.scene.addLine( + 10, + daq_utils.screenPixY - 30, + 10 + self.imageScaleLineLen, + daq_utils.screenPixY - 30, + scalePen, + ) + self.imageScaleText = self.scene.addSimpleText( + "50 microns", font=QtGui.QFont("Times", 13) + ) + self.imageScaleText.setPen(scaleTextPen) + self.imageScaleText.setPos(10, 450) + self.click_positions = [] + hBoxHutchVidsLayout.addWidget(self.viewHutchTop) + hBoxHutchVidsLayout.addWidget(self.viewHutchCorner) + vBoxVidLayout.addLayout(hBoxHutchVidsLayout) + vBoxVidLayout.addWidget(self.view) + hBoxSampleOrientationLayout = QtWidgets.QHBoxLayout() + setDC2CPButton = QtWidgets.QPushButton("SetStart") + setDC2CPButton.setFixedWidth(50) + setDC2CPButton.clicked.connect(self.setDCStartCB) + omegaLabel = QtWidgets.QLabel("Omega:") + #omegaMonitorPV = str(getBlConfig("omegaMonitorPV")) + self.sampleOmegaRBVLedit = QtEpicsPVLabel( + self.gon.omega.readback.pvname, self, 70 + ) + omegaSPLabel = QtWidgets.QLabel("SetPoint:") + omegaSPLabel.setFixedWidth(70) + self.sampleOmegaMoveLedit = QtEpicsPVEntry( + self.gon.omega.setpoint.pvname, self, 70, 2 + ) + self.sampleOmegaMoveLedit.getEntry().returnPressed.connect(self.moveOmegaCB) + moveOmegaButton = QtWidgets.QPushButton("Move") + moveOmegaButton.clicked.connect(self.moveOmegaCB) + omegaTweakNegButtonSuperFine = QtWidgets.QPushButton("-1") + omegaTweakNegButtonFine = QtWidgets.QPushButton("-5") + omegaTweakNegButton = QtWidgets.QPushButton("<") + omegaTweakNegButton.clicked.connect(self.omegaTweakNegCB) + omegaTweakNegButtonFine.clicked.connect( + functools.partial(self.omegaTweakCB, -5) + ) + omegaTweakNegButtonSuperFine.clicked.connect( + functools.partial(self.omegaTweakCB, -1) + ) + self.omegaTweakVal_ledit = QtWidgets.QLineEdit() + self.omegaTweakVal_ledit.setFixedWidth(45) + self.omegaTweakVal_ledit.setText("90") + omegaTweakPosButtonSuperFine = QtWidgets.QPushButton("+1") + omegaTweakPosButtonFine = QtWidgets.QPushButton("+5") + omegaTweakPosButton = QtWidgets.QPushButton(">") + omegaTweakPosButton.clicked.connect(self.omegaTweakPosCB) + omegaTweakPosButtonFine.clicked.connect(functools.partial(self.omegaTweakCB, 5)) + omegaTweakPosButtonSuperFine.clicked.connect( + functools.partial(self.omegaTweakCB, 1) + ) + hBoxSampleOrientationLayout.addWidget(setDC2CPButton) + hBoxSampleOrientationLayout.addWidget(omegaLabel) + hBoxSampleOrientationLayout.addWidget(self.sampleOmegaRBVLedit.getEntry()) + hBoxSampleOrientationLayout.addWidget(omegaSPLabel) + hBoxSampleOrientationLayout.addWidget(self.sampleOmegaMoveLedit.getEntry()) + spacerItem = QtWidgets.QSpacerItem( + 50, 1, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum + ) + hBoxSampleOrientationLayout.insertSpacing(5, 50) + hBoxSampleOrientationLayout.addWidget(omegaTweakNegButtonSuperFine) + hBoxSampleOrientationLayout.addWidget(omegaTweakNegButtonFine) + hBoxSampleOrientationLayout.addWidget(omegaTweakNegButton) + hBoxSampleOrientationLayout.addWidget(self.omegaTweakVal_ledit) + hBoxSampleOrientationLayout.addWidget(omegaTweakPosButton) + hBoxSampleOrientationLayout.addWidget(omegaTweakPosButtonFine) + hBoxSampleOrientationLayout.addWidget(omegaTweakPosButtonSuperFine) + hBoxSampleOrientationLayout.addStretch(1) + hBoxVidControlLayout = QtWidgets.QHBoxLayout() + lightLevelLabel = QtWidgets.QLabel("Light") + lightLevelLabel.setAlignment(QtCore.Qt.AlignRight | Qt.AlignVCenter) + sampleBrighterButton = QtWidgets.QPushButton("+") + sampleBrighterButton.setFixedWidth(30) + sampleBrighterButton.clicked.connect(self.lightUpCB) + sampleDimmerButton = QtWidgets.QPushButton("-") + sampleDimmerButton.setFixedWidth(30) + sampleDimmerButton.clicked.connect(self.lightDimCB) + focusLabel = QtWidgets.QLabel("Focus") + focusLabel.setAlignment(QtCore.Qt.AlignRight | Qt.AlignVCenter) + focusPlusButton = QtWidgets.QPushButton("+") + focusPlusButton.setFixedWidth(30) + focusPlusButton.clicked.connect(functools.partial(self.focusTweakCB, 5)) + focusMinusButton = QtWidgets.QPushButton("-") + focusMinusButton.setFixedWidth(30) + focusMinusButton.clicked.connect(functools.partial(self.focusTweakCB, -5)) + annealButton = QtWidgets.QPushButton("Anneal") + annealButton.clicked.connect(self.annealButtonCB) + annealTimeLabel = QtWidgets.QLabel("Time") + self.annealTime_ledit = QtWidgets.QLineEdit() + self.annealTime_ledit.setFixedWidth(40) + self.annealTime_ledit.setText("0.5") + magLevelLabel = QtWidgets.QLabel("Vid:") + snapshotButton = QtWidgets.QPushButton("SnapShot") + snapshotButton.clicked.connect(self.saveVidSnapshotButtonCB) + self.hideRastersCheckBox = QCheckBox("Hide\nRasters") + self.hideRastersCheckBox.setChecked(False) + self.hideRastersCheckBox.stateChanged.connect(self.hideRastersCB) + hBoxVidControlLayout.addWidget(self.zoom1Radio) + if ( + daq_utils.sampleCameraCount >= 2 + ): # Button naming assumes sequential camera ordering + hBoxVidControlLayout.addWidget(self.zoom2Radio) + if daq_utils.sampleCameraCount >= 3: + hBoxVidControlLayout.addWidget(self.zoom3Radio) + if daq_utils.sampleCameraCount >= 4: + hBoxVidControlLayout.addWidget(self.zoom4Radio) + else: + hBoxVidControlLayout.addLayout(hBoxZoomLevelLayout) + hBoxVidControlLayout.addWidget(focusLabel) + hBoxVidControlLayout.addWidget(focusPlusButton) + hBoxVidControlLayout.addWidget(focusMinusButton) + hBoxVidControlLayout.addWidget(lightLevelLabel) + hBoxVidControlLayout.addWidget(sampleBrighterButton) + hBoxVidControlLayout.addWidget(sampleDimmerButton) + hBoxVidControlLayout.addWidget(annealButton) + hBoxVidControlLayout.addWidget(annealTimeLabel) + hBoxVidControlLayout.addWidget(self.annealTime_ledit) + hBoxSampleAlignLayout = QtWidgets.QHBoxLayout() + centerLoopButton = QtWidgets.QPushButton("Center\nLoop") + centerLoopButton.clicked.connect(self.autoCenterLoopCB) + measureButton = QtWidgets.QPushButton("Measure") + measureButton.clicked.connect(self.measurePolyCB) + loopShapeButton = QtWidgets.QPushButton("Add Raster\nto Queue") + loopShapeButton.clicked.connect(self.drawInteractiveRasterCB) + runRastersButton = QtWidgets.QPushButton("Run\nRaster") + runRastersButton.clicked.connect(self.runRastersCB) + clearGraphicsButton = QtWidgets.QPushButton("Clear") + clearGraphicsButton.clicked.connect(self.eraseCB) + self.click3Button = QtWidgets.QPushButton("3-Click\nCenter") + self.click3Button.clicked.connect(self.center3LoopCB) + + self.threeClickCount = 0 + saveCenteringButton = QtWidgets.QPushButton("Save\nCenter") + saveCenteringButton.clicked.connect(self.saveCenterCB) + selectAllCenteringButton = QtWidgets.QPushButton("Select All\nCenterings") + selectAllCenteringButton.clicked.connect(self.selectAllCenterCB) + hBoxSampleAlignLayout.addWidget(centerLoopButton) + hBoxSampleAlignLayout.addWidget(clearGraphicsButton) + hBoxSampleAlignLayout.addWidget(saveCenteringButton) + hBoxSampleAlignLayout.addWidget(selectAllCenteringButton) + hBoxSampleAlignLayout.addWidget(self.click3Button) + hBoxSampleAlignLayout.addWidget(snapshotButton) + hBoxSampleAlignLayout.addWidget(self.hideRastersCheckBox) + self.click3Button.setMaximumSize(self.click3Button.sizeHint()) + hBoxRadioLayout100 = QtWidgets.QHBoxLayout() + vidActionLabel = QtWidgets.QLabel("Video Click Mode:") + self.vidActionRadioGroup = QtWidgets.QButtonGroup() + self.vidActionC2CRadio = QtWidgets.QRadioButton("C2C") + self.vidActionC2CRadio.setChecked(True) + self.vidActionC2CRadio.toggled.connect(self.vidActionToggledCB) + self.vidActionRadioGroup.addButton(self.vidActionC2CRadio) + self.vidActionDefineCenterRadio = QtWidgets.QRadioButton("Define Center") + self.vidActionDefineCenterRadio.setChecked(False) + self.vidActionDefineCenterRadio.setEnabled(False) + self.vidActionDefineCenterRadio.toggled.connect(self.vidActionToggledCB) + self.vidActionRadioGroup.addButton(self.vidActionDefineCenterRadio) + self.vidActionRasterExploreRadio = QtWidgets.QRadioButton("Raster Explore") + self.vidActionRasterExploreRadio.setChecked(False) + self.vidActionRasterExploreRadio.toggled.connect(self.vidActionToggledCB) + self.vidActionRadioGroup.addButton(self.vidActionRasterExploreRadio) + self.vidActionRasterSelectRadio = QtWidgets.QRadioButton("Raster Select") + self.vidActionRasterSelectRadio.setChecked(False) + self.vidActionRasterSelectRadio.toggled.connect(self.vidActionToggledCB) + self.vidActionRadioGroup.addButton(self.vidActionRasterSelectRadio) + self.vidActionRasterDefRadio = QtWidgets.QRadioButton("Define Raster") + self.vidActionRasterDefRadio.setChecked(False) + self.vidActionRasterDefRadio.setEnabled(False) + self.vidActionRasterDefRadio.toggled.connect(self.vidActionToggledCB) + self.vidActionRadioGroup.addButton(self.vidActionRasterDefRadio) + hBoxRadioLayout100.addWidget(vidActionLabel) + hBoxRadioLayout100.addWidget(self.vidActionC2CRadio) + hBoxRadioLayout100.addWidget(self.vidActionRasterExploreRadio) + hBoxRadioLayout100.addWidget(self.vidActionRasterDefRadio) + hBoxRadioLayout100.addWidget(self.vidActionDefineCenterRadio) + vBoxVidLayout.addLayout(hBoxSampleOrientationLayout) + vBoxVidLayout.addLayout(hBoxVidControlLayout) + vBoxVidLayout.addLayout(hBoxSampleAlignLayout) + vBoxVidLayout.addLayout(hBoxRadioLayout100) + self.VidFrame.setLayout(vBoxVidLayout) + splitter11.addWidget(self.mainSetupFrame) + self.colTabs = QtWidgets.QTabWidget() + self.energyFrame = QFrame() + vBoxEScanFull = QtWidgets.QVBoxLayout() + hBoxEScan = QtWidgets.QHBoxLayout() + vBoxEScan = QtWidgets.QVBoxLayout() + self.periodicTable = QPeriodicTable(butSize=20) + self.periodicTable.elementClicked("Se") + vBoxEScan.addWidget(self.periodicTable) + self.EScanDataPathGB = DataLocInfo(self) + vBoxEScan.addWidget(self.EScanDataPathGB) + hBoxEScanParams = QtWidgets.QHBoxLayout() + hBoxEScanButtons = QtWidgets.QHBoxLayout() + tempPlotButton = QtWidgets.QPushButton("Queue Requests") + tempPlotButton.clicked.connect(self.queueEnScanCB) + clearEnscanPlotButton = QtWidgets.QPushButton("Clear") + clearEnscanPlotButton.clicked.connect(self.clearEnScanPlotCB) + hBoxEScanButtons.addWidget(clearEnscanPlotButton) + hBoxEScanButtons.addWidget(tempPlotButton) + escanStepsLabel = QtWidgets.QLabel("Steps") + self.escan_steps_ledit = QtWidgets.QLineEdit() + self.escan_steps_ledit.setText("41") + escanStepsizeLabel = QtWidgets.QLabel("Stepsize (EVs)") + self.escan_stepsize_ledit = QtWidgets.QLineEdit() + self.escan_stepsize_ledit.setText("1") + hBoxEScanParams.addWidget(escanStepsLabel) + hBoxEScanParams.addWidget(self.escan_steps_ledit) + hBoxEScanParams.addWidget(escanStepsizeLabel) + hBoxEScanParams.addWidget(self.escan_stepsize_ledit) + hBoxChoochResults = QtWidgets.QHBoxLayout() + hBoxChoochResults2 = QtWidgets.QHBoxLayout() + choochResultsLabel = QtWidgets.QLabel("Chooch Results") + choochInflLabel = QtWidgets.QLabel("Infl") + self.choochInfl = QtWidgets.QLabel("") + self.choochInfl.setFixedWidth(70) + choochPeakLabel = QtWidgets.QLabel("Peak") + self.choochPeak = QtWidgets.QLabel("") + self.choochPeak.setFixedWidth(70) + choochInflFPrimeLabel = QtWidgets.QLabel("fPrimeInfl") + self.choochFPrimeInfl = QtWidgets.QLabel("") + self.choochFPrimeInfl.setFixedWidth(70) + choochInflF2PrimeLabel = QtWidgets.QLabel("f2PrimeInfl") + self.choochF2PrimeInfl = QtWidgets.QLabel("") + self.choochF2PrimeInfl.setFixedWidth(70) + choochPeakFPrimeLabel = QtWidgets.QLabel("fPrimePeak") + self.choochFPrimePeak = QtWidgets.QLabel("") + self.choochFPrimePeak.setFixedWidth(70) + choochPeakF2PrimeLabel = QtWidgets.QLabel("f2PrimePeak") + self.choochF2PrimePeak = QtWidgets.QLabel("") + self.choochF2PrimePeak.setFixedWidth(70) + hBoxChoochResults.addWidget(choochResultsLabel) + hBoxChoochResults.addWidget(choochInflLabel) + hBoxChoochResults.addWidget(self.choochInfl) + hBoxChoochResults.addWidget(choochPeakLabel) + hBoxChoochResults.addWidget(self.choochPeak) + hBoxChoochResults2.addWidget(choochInflFPrimeLabel) + hBoxChoochResults2.addWidget(self.choochFPrimeInfl) + hBoxChoochResults2.addWidget(choochInflF2PrimeLabel) + hBoxChoochResults2.addWidget(self.choochF2PrimeInfl) + hBoxChoochResults2.addWidget(choochPeakFPrimeLabel) + hBoxChoochResults2.addWidget(self.choochFPrimePeak) + hBoxChoochResults2.addWidget(choochPeakF2PrimeLabel) + hBoxChoochResults2.addWidget(self.choochF2PrimePeak) + vBoxEScan.addLayout(hBoxEScanParams) + vBoxEScan.addLayout(hBoxEScanButtons) + vBoxEScan.addLayout(hBoxChoochResults) + vBoxEScan.addLayout(hBoxChoochResults2) + hBoxEScan.addLayout(vBoxEScan) + verticalLine = QFrame() + verticalLine.setFrameStyle(QFrame.VLine) + self.EScanGraph = ScanWindow(self.energyFrame) + hBoxEScan.addWidget(verticalLine) + hBoxEScan.addWidget(self.EScanGraph) + vBoxEScanFull.addLayout(hBoxEScan) + self.choochGraph = ScanWindow( + self.energyFrame + ) # TODO should be another type? need to be able to add curves + vBoxEScanFull.addWidget(self.choochGraph) + self.energyFrame.setLayout(vBoxEScanFull) + splitter11.addWidget(self.VidFrame) + self.colTabs.addTab(splitter11, "Sample Control") + self.colTabs.addTab(self.energyFrame, "Energy Scan") + splitter1.addWidget(self.colTabs) + vBoxlayout.addWidget(splitter1) + self.lastFileLabel2 = QtWidgets.QLabel("File:") + self.lastFileLabel2.setFixedWidth(60) + if daq_utils.beamline == "amx": + self.lastFileRBV2 = QtEpicsPVLabel( + "XF:17IDB-ES:AMX{Det:Eig9M}cam1:FullFileName_RBV", self, 0 + ) + else: + self.lastFileRBV2 = QtEpicsPVLabel( + "XF:17IDC-ES:FMX{Det:Eig16M}cam1:FullFileName_RBV", self, 0 + ) + fileHBoxLayout = QtWidgets.QHBoxLayout() + fileHBoxLayout2 = QtWidgets.QHBoxLayout() + self.controlMasterCheckBox = QCheckBox("Control Master") + self.controlMasterCheckBox.stateChanged.connect(self.changeControlMasterCB) + self.controlMasterCheckBox.setChecked(False) + fileHBoxLayout.addWidget(self.controlMasterCheckBox) + self.statusLabel = QtEpicsPVLabel( + daq_utils.beamlineComm + "program_state", + self, + 150, + highlight_on_change=False, + ) + fileHBoxLayout.addWidget(self.statusLabel.getEntry()) + self.shutterStateLabel = QtWidgets.QLabel("Shutter State:") + governorMessageLabel = QtWidgets.QLabel("Governor Message:") + self.governorMessage = QtEpicsPVLabel( + daq_utils.pvLookupDict["governorMessage"], + self, + 140, + highlight_on_change=False, + ) + ringCurrentMessageLabel = QtWidgets.QLabel("Ring(mA):") + self.ringCurrentMessage = QtWidgets.QLabel(str(self.ringCurrent_pv.get())) + beamAvailable = self.beamAvailable_pv.get() + if beamAvailable: + self.beamAvailLabel = QtWidgets.QLabel("Beam Available") + self.beamAvailLabel.setStyleSheet("background-color: #99FF66;") + else: + self.beamAvailLabel = QtWidgets.QLabel("No Beam") + self.beamAvailLabel.setStyleSheet("background-color: red;") + sampleExposed = self.sampleExposed_pv.get() + if sampleExposed: + self.sampleExposedLabel = QtWidgets.QLabel("Sample Exposed") + self.sampleExposedLabel.setStyleSheet("background-color: red;") + else: + self.sampleExposedLabel = QtWidgets.QLabel("Sample Not Exposed") + self.sampleExposedLabel.setStyleSheet("background-color: #99FF66;") + gripperLabel = QtWidgets.QLabel("Gripper Temp:") + if daq_utils.beamline == "nyx": + self.gripperTempLabel = QtWidgets.QLabel("N/A") + else: + self.gripperTempLabel = QtWidgets.QLabel("%.1f" % self.gripTemp_pv.get()) + cryostreamLabel = QtWidgets.QLabel("Cryostream Temp:") + if getBlConfig(CRYOSTREAM_ONLINE): + self.cryostreamTempLabel = QtWidgets.QLabel( + str(self.cryostreamTemp_pv.get()) + ) + else: + self.cryostreamTempLabel = QtWidgets.QLabel("N/A") + + fileHBoxLayout.addWidget(gripperLabel) + fileHBoxLayout.addWidget(self.gripperTempLabel) + fileHBoxLayout.addWidget(cryostreamLabel) + fileHBoxLayout.addWidget(self.cryostreamTempLabel) + fileHBoxLayout.addWidget(ringCurrentMessageLabel) + fileHBoxLayout.addWidget(self.ringCurrentMessage) + fileHBoxLayout.addWidget(self.beamAvailLabel) + fileHBoxLayout.addWidget(self.sampleExposedLabel) + fileHBoxLayout.addWidget(governorMessageLabel) + fileHBoxLayout.addWidget(self.governorMessage.getEntry()) + fileHBoxLayout2.addWidget(self.lastFileLabel2) + fileHBoxLayout2.addWidget(self.lastFileRBV2.getEntry()) + vBoxlayout.addLayout(fileHBoxLayout) + vBoxlayout.addLayout(fileHBoxLayout2) + sampleTab.setLayout(vBoxlayout) + self.tabs.addTab(sampleTab, "Collect") + # 12/19 - uncomment this to expose the PyMCA XRF interface. It's not connected to anything. + self.zoomLevelToggledCB("Zoom1") + + if daq_utils.beamline == "nyx": # hiding unused GUI elements + self.protoRasterRadio.setVisible(False) + self.protoStandardRadio.setVisible(False) + self.protoVectorRadio.setVisible(False) + self.protoOtherRadio.setVisible(False) + self.autoProcessingCheckBox.setVisible(False) + self.fastEPCheckBox.setVisible(False) + self.dimpleCheckBox.setVisible(False) + self.centeringComboBox.setVisible(False) + annealButton.setVisible(False) + centerLoopButton.setVisible(False) + clearGraphicsButton.setVisible(False) + saveCenteringButton.setVisible(False) + selectAllCenteringButton.setVisible(False) + snapshotButton.setVisible(False) + annealTimeLabel.setVisible(False) + self.annealTime_ledit.setVisible(False) + self.vidActionDefineCenterRadio.setVisible(False) + self.hideRastersCheckBox.setEnabled(True) + self.vidActionC2CRadio.setEnabled(True) + self.vidActionRasterExploreRadio.setEnabled(True) + self.vidActionRasterDefRadio.setEnabled(True) + + + + #self.captureLowMag = cv2.VideoCapture(daq_utils.lowMagCamURL) + #self.captureLowMag.set(cv2.CAP_PROP_BUFFERSIZE, 1) + self.captureLowMag = daq_utils.lowMagCamURL + self.capture = self.captureLowMag + + #self.sampleCameraThread = VideoThread( + # parent=self, delay=SAMPLE_TIMER_DELAY, camera_object=self.capture + #) + self.sampleCameraThread = VideoThread( + parent=self, delay=HUTCH_TIMER_DELAY, url=daq_utils.highMagCamURL + ) + self.sampleCameraThread.frame_ready.connect( + lambda frame: self.updateCam(self.pixmap_item, frame) + ) + self.sampleCameraThread.start() + + + + self.hutchCornerCamThread = VideoThread( + parent=self, delay=HUTCH_TIMER_DELAY, url=getBlConfig("hutchCornerCamURL") + ) + self.hutchCornerCamThread.frame_ready.connect( + lambda frame: self.updateCam(self.pixmap_item_HutchCorner, frame) + ) + self.hutchCornerCamThread.start() + + self.hutchTopCamThread = VideoThread( + parent=self, delay=HUTCH_TIMER_DELAY, url=getBlConfig("hutchTopCamURL") + ) + self.hutchTopCamThread.frame_ready.connect( + lambda frame: self.updateCam(self.pixmap_item_HutchTop, frame) + ) + self.hutchTopCamThread.start() + serverCheckThread = ServerCheckThread( + parent=self, delay=SERVER_CHECK_DELAY) + serverCheckThread.visit_dir_changed.connect(QApplication.instance().quit) + serverCheckThread.start() + + def updateCam(self, pixmapItem: "QGraphicsPixmapItem", frame): + pixmapItem.setPixmap(frame) + + def albulaCheckCB(self, state): + if state != QtCore.Qt.Checked: + albulaUtils.albulaClose() + else: + albulaUtils.albulaOpen() # TODO there is no albulaOpen method! remove? + + def annealButtonCB(self): + try: + ftime = float(self.annealTime_ledit.text()) + if ftime >= 0.1 and ftime <= 5.0: + comm_s = "anneal(" + str(ftime) + ")" + logger.info(comm_s) + self.send_to_server(comm_s) + else: + self.popupServerMessage( + "Anneal time must be between 0.1 and 5.0 seconds." + ) + except: + pass + + def hideRastersCB(self, state): + if state == QtCore.Qt.Checked: + self.eraseRastersCB() + else: + self.refreshCollectionParams(self.selectedSampleRequest) + + def stillModeUserPushCB(self, state): + logger.info("still checkbox state " + str(state)) + if self.controlEnabled(): + if state: + self.stillMode_pv.put(1) + self.setGuiValues({"osc_range": "0.0"}) + else: + self.standardMode_pv.put(1) + else: + self.popupServerMessage("You don't have control") + if self.stillModeStatePV.get(): + self.stillModeCheckBox.setChecked(True) + else: + self.stillModeCheckBox.setChecked(False) + + def autoProcessingCheckCB(self, state): + if state == QtCore.Qt.Checked: + self.dimpleCheckBox.setEnabled(True) + self.xia2CheckBox.setEnabled(True) + else: + self.fastEPCheckBox.setEnabled(False) + self.dimpleCheckBox.setEnabled(False) + self.xia2CheckBox.setEnabled(False) + + def rasterGrainToggledCB(self, identifier): + if identifier == "Coarse" or identifier == "Fine" or identifier == "VFine": + cellSize = self.rasterStepDefs[identifier] + self.rasterStepEdit.setText(str(cellSize)) + self.beamWidth_ledit.setText(str(cellSize)) + self.beamHeight_ledit.setText(str(cellSize)) + + def vidActionToggledCB(self): + if len(self.rasterList) > 0: + if self.vidActionRasterSelectRadio.isChecked(): + for i in range(len(self.rasterList)): + if self.rasterList[i] != None: + self.rasterList[i]["graphicsItem"].setFlag( + QtWidgets.QGraphicsItem.ItemIsSelectable, True + ) + else: + for i in range(len(self.rasterList)): + if self.rasterList[i] != None: + self.rasterList[i]["graphicsItem"].setFlag( + QtWidgets.QGraphicsItem.ItemIsMovable, False + ) + self.rasterList[i]["graphicsItem"].setFlag( + QtWidgets.QGraphicsItem.ItemIsSelectable, False + ) + if self.vidActionRasterDefRadio.isChecked(): + self.click_positions = [] + self.showProtParams() + if self.vidActionC2CRadio.isChecked(): + self.click_positions = [] + if ( + self.protoComboBox.findText(str("raster")) + == self.protoComboBox.currentIndex() + or self.protoComboBox.findText(str("stepRaster")) + == self.protoComboBox.currentIndex() + ): + self.protoComboBox.setCurrentIndex( + self.protoComboBox.findText(str("standard")) + ) + self.protoComboActivatedCB("standard") + self.showProtParams() + + def adjustGraphics4ZoomChange(self, fov): + imageScaleMicrons = ( + int(round(self.imageScaleLineLen * (fov["x"] / daq_utils.screenPixX))) + * daq_utils.unitScaling + ) + self.imageScaleText.setText(str(imageScaleMicrons) + " microns") + if self.rasterList != []: + saveRasterList = self.rasterList + self.eraseDisplayCB() + for i in range(len(saveRasterList)): + if saveRasterList[i] == None: + self.rasterList.append(None) + else: + rasterXPixels = float(saveRasterList[i]["graphicsItem"].x()) + rasterYPixels = float(saveRasterList[i]["graphicsItem"].y()) + self.rasterXmicrons = rasterXPixels * ( + fov["x"] / daq_utils.screenPixX + ) + self.rasterYmicrons = rasterYPixels * ( + fov["y"] / daq_utils.screenPixY + ) + if not self.hideRastersCheckBox.isChecked(): + self.drawPolyRaster( + db_lib.getRequestByID(saveRasterList[i]["uid"]), + saveRasterList[i]["coords"]["x"], + saveRasterList[i]["coords"]["y"], + saveRasterList[i]["coords"]["z"], + ) + self.fillPolyRaster( + db_lib.getRequestByID(saveRasterList[i]["uid"]) + ) + self.processSampMove(self.gon.x.val(), "x") + self.processSampMove(self.gon.y.val(), "y") + self.processSampMove(self.gon.z.val(), "z") + if self.vectorStart != None: + self.processSampMove(self.gon.x.val(), "x") + self.processSampMove(self.gon.y.val(), "y") + self.processSampMove(self.gon.z.val(), "z") + if self.centeringMarksList != []: + self.processSampMove(self.gon.x.val(), "x") + self.processSampMove(self.gon.y.val(), "y") + self.processSampMove(self.gon.z.val(), "z") + + def flushBuffer(self, vidStream): + if vidStream == None: + return + for i in range(0, 1000): + stime = time.time() + vidStream.grab() + etime = time.time() + commTime = etime - stime + if commTime > 0.01: + return + + def zoomLevelComboActivatedCB(self, identifier): + self.camera.zoom.put(identifier) + self.centerMarker.setPos(self.getMD2BeamCenterX()-self.centerMarkerCharOffsetX, self.getMD2BeamCenterY()-self.centerMarkerCharOffsetY) + #self.flushBuffer(self.capture) + #self.capture.release() + #self.capture = cv2.VideoCapture(daq_utils.lowMagZoomCamURL) + + def zoomLevelToggledCB(self, identifier): + fov = {} + zoomedCursorX = self.getMD2BeamCenterX() - self.centerMarkerCharOffsetX + zoomedCursorY = self.getMD2BeamCenterY() - self.centerMarkerCharOffsetY + if self.zoom2Radio.isChecked(): + self.flushBuffer(self.captureLowMagZoom) + self.capture = self.captureLowMagZoom + fov["x"] = daq_utils.lowMagFOVx / 2.0 + fov["y"] = daq_utils.lowMagFOVy / 2.0 + unzoomedCursorX = self.lowMagCursorX_pv.get() - self.centerMarkerCharOffsetX + unzoomedCursorY = self.lowMagCursorY_pv.get() - self.centerMarkerCharOffsetY + if unzoomedCursorX * 2.0 < self.getMD2BeamCenterX(): + zoomedCursorX = unzoomedCursorX * 2.0 + if unzoomedCursorY * 2.0 < self.getMD2BeamCenterY(): + zoomedCursorY = unzoomedCursorY * 2.0 + if ( + unzoomedCursorX - self.getMD2BeamCenterX() + > self.getMD2BeamCenterX() / 2 + ): + zoomedCursorX = (unzoomedCursorX * 2.0) - daq_utils.screenPixX + if ( + unzoomedCursorY - self.getMD2BeamCenterY() + > self.getMD2BeamCenterY() / 2 + ): + zoomedCursorY = (unzoomedCursorY * 2.0) - daq_utils.screenPixY + self.centerMarker.setPos(zoomedCursorX, zoomedCursorY) + self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) + self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) + self.beamSizeOverlay.setRect( + self.overlayPosOffsetX + + self.centerMarker.x() + - (self.beamSizeXPixels / 2), + self.overlayPosOffsetY + + self.centerMarker.y() + - (self.beamSizeYPixels / 2), + self.beamSizeXPixels, + self.beamSizeYPixels, + ) + elif self.zoom1Radio.isChecked(): + self.flushBuffer(self.captureLowMag) + self.capture = self.captureLowMag + fov["x"] = daq_utils.lowMagFOVx + fov["y"] = daq_utils.lowMagFOVy + self.centerMarker.setPos( + self.lowMagCursorX_pv.get() - self.centerMarkerCharOffsetX, + self.lowMagCursorY_pv.get() - self.centerMarkerCharOffsetY, + ) + self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) + self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) + self.beamSizeOverlay.setRect( + self.overlayPosOffsetX + + self.centerMarker.x() + - (self.beamSizeXPixels / 2), + self.overlayPosOffsetY + + self.centerMarker.y() + - (self.beamSizeYPixels / 2), + self.beamSizeXPixels, + self.beamSizeYPixels, + ) + elif self.zoom4Radio.isChecked(): + self.flushBuffer(self.captureHighMagZoom) + self.capture = self.captureHighMagZoom + fov["x"] = daq_utils.highMagFOVx / 2.0 + fov["y"] = daq_utils.highMagFOVy / 2.0 + unzoomedCursorX = ( + self.highMagCursorX_pv.get() - self.centerMarkerCharOffsetX + ) + unzoomedCursorY = ( + self.highMagCursorY_pv.get() - self.centerMarkerCharOffsetY + ) + if unzoomedCursorX * 2.0 < self.getMD2BeamCenterX(): + zoomedCursorX = unzoomedCursorX * 2.0 + if unzoomedCursorY * 2.0 < self.getMD2BeamCenterY(): + zoomedCursorY = unzoomedCursorY * 2.0 + if ( + unzoomedCursorX - self.getMD2BeamCenterX() + > self.getMD2BeamCenterX() / 2 + ): + zoomedCursorX = (unzoomedCursorX * 2.0) - daq_utils.screenPixX + if ( + unzoomedCursorY - self.getMD2BeamCenterY() + > self.getMD2BeamCenterY() / 2 + ): + zoomedCursorY = (unzoomedCursorY * 2.0) - daq_utils.screenPixY + self.centerMarker.setPos(zoomedCursorX, zoomedCursorY) + self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) + self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) + self.beamSizeOverlay.setRect( + self.overlayPosOffsetX + + self.centerMarker.x() + - (self.beamSizeXPixels / 2), + self.overlayPosOffsetY + + self.centerMarker.y() + - (self.beamSizeYPixels / 2), + self.beamSizeXPixels, + self.beamSizeYPixels, + ) + elif self.zoom3Radio.isChecked(): + self.flushBuffer(self.captureHighMag) + self.capture = self.captureHighMag + fov["x"] = daq_utils.highMagFOVx + fov["y"] = daq_utils.highMagFOVy + self.centerMarker.setPos( + self.highMagCursorX_pv.get() - self.centerMarkerCharOffsetX, + self.highMagCursorY_pv.get() - self.centerMarkerCharOffsetY, + ) + self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) + self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) + self.beamSizeOverlay.setRect( + self.overlayPosOffsetX + + self.centerMarker.x() + - (self.beamSizeXPixels / 2), + self.overlayPosOffsetY + + self.centerMarker.y() + - (self.beamSizeYPixels / 2), + self.beamSizeXPixels, + self.beamSizeYPixels, + ) + self.adjustGraphics4ZoomChange(fov) + self.sampleZoomChangeSignal.emit(self.capture) + + def saveVidSnapshotButtonCB(self): + comment, useOlog, ok = SnapCommentDialog.getComment() + if ok: + self.saveVidSnapshotCB(comment, useOlog) + + def saveVidSnapshotCB( + self, comment="", useOlog=False, reqID=None, rasterHeatJpeg=None + ): + if not os.path.exists("snapshots"): + os.system("mkdir snapshots") + width = 640 + height = 512 + targetrect = QRectF(0, 0, width, height) + sourcerect = QRectF(0, 0, width, height) + pix = QtGui.QPixmap(width, height) + painter = QtGui.QPainter(pix) + self.scene.render(painter, targetrect, sourcerect) + painter.end() + now = time.time() + if rasterHeatJpeg == None: + if reqID != None: + filePrefix = db_lib.getRequestByID(reqID)["request_obj"]["file_prefix"] + imagePath = ( + f"{getBlConfig('visitDirectory')}/snapshots/{filePrefix}{int(now)}.jpg" + ) + else: + if self.dataPathGB.prefix_ledit.text() != "": + imagePath = ( + f"{getBlConfig('visitDirectory')}/snapshots/{self.dataPathGB.prefix_ledit.text()}{int(now)}.jpg" + ) + else: + imagePath = ( + f"{getBlConfig('visitDirectory')}/snapshots/capture{int(now)}.jpg" + ) + else: + imagePath = rasterHeatJpeg + logger.info("saving " + imagePath) + pix.save(imagePath, "JPG") + if useOlog: + lsdcOlog.toOlogPicture(imagePath, str(comment)) + resultObj = {} + imgRef = imagePath # for now, just the path, might want to use filestore later, if they really do facilitate moving files + resultObj["data"] = imgRef + resultObj["comment"] = str(comment) + if ( + reqID != None + ): # assuming raster here, but will probably need to check the type + db_lib.addResultforRequest( + "rasterJpeg", + reqID, + owner=daq_utils.owner, + result_obj=resultObj, + proposalID=daq_utils.getProposalID(), + beamline=daq_utils.beamline, + ) + else: # the user pushed the snapshot button on the gui + mountedSampleID = self.mountedPin_pv.get() + if mountedSampleID != "": + db_lib.addResulttoSample( + "snapshotResult", + mountedSampleID, + owner=daq_utils.owner, + result_obj=resultObj, + proposalID=daq_utils.getProposalID(), + beamline=daq_utils.beamline, + ) + else: # beamline result, no sample mounted + db_lib.addResulttoBL( + "snapshotResult", + daq_utils.beamline, + owner=daq_utils.owner, + result_obj=resultObj, + proposalID=daq_utils.getProposalID(), + ) + + def changeControlMasterCB( + self, state, processID=os.getpid() + ): # when someone touches checkbox, either through interaction or code + logger.info("change control master") + logger.info(processID) + currentMaster = self.controlMaster_pv.get() + if currentMaster < 0: + self.controlMaster_pv.put( + currentMaster + ) # this makes sure if things are locked, and someone tries to get control, their checkbox will uncheck itself + self.popupServerMessage("Control is locked by staff. Please stand by.") + return + if state == QtCore.Qt.Checked: + self.controlMaster_pv.put(processID) + if ( + len(self.osc_range_ledit.text()) == 0 + or abs(float(self.osc_range_ledit.text())) > 0 + ): + self.standardMode_pv.put(1) + elif float(self.osc_range_ledit.text()) == 0: + self.stillMode_pv.put(1) + else: + self.userScreenDialog.hide() + if self.staffScreenDialog != None: + self.staffScreenDialog.hide() + + def calculateNewYCoordPos(self, startYX, startYY): + startY_pixels = 0 + zMotRBV = self.motPos["y"] + yMotRBV = self.motPos["z"] + if self.scannerType == "PI": + fineYRBV = self.motPos["fineY"] + fineZRBV = self.motPos["fineZ"] + deltaYX = startYX - zMotRBV - fineZRBV + deltaYY = startYY - yMotRBV - fineYRBV + else: + deltaYX = startYX - zMotRBV + deltaYY = startYY - yMotRBV + omegaRad = math.radians(self.motPos["omega"]) + newYY = ( + float(startY_pixels - (self.screenYmicrons2pixels(deltaYY))) + ) * math.sin(omegaRad) + newYX = ( + float(startY_pixels - (self.screenYmicrons2pixels(deltaYX))) + ) * math.cos(omegaRad) + newY = newYX + newYY + return newY + + def processROIChange(self, posRBV, ID): + pass + + def processLowMagCursorChange(self, posRBV, ID): + zoomedCursorX = self.getMD2BeamCenterX() - self.centerMarkerCharOffsetX + zoomedCursorY = self.getMD2BeamCenterY() - self.centerMarkerCharOffsetY + if self.zoom2Radio.isChecked(): # lowmagzoom + unzoomedCursorX = self.lowMagCursorX_pv.get() - self.centerMarkerCharOffsetX + unzoomedCursorY = self.lowMagCursorY_pv.get() - self.centerMarkerCharOffsetY + if unzoomedCursorX * 2.0 < self.getMD2BeamCenterX(): + zoomedCursorX = unzoomedCursorX * 2.0 + if unzoomedCursorY * 2.0 < self.getMD2BeamCenterY(): + zoomedCursorY = unzoomedCursorY * 2.0 + if ( + unzoomedCursorX - self.getMD2BeamCenterX() + > self.getMD2BeamCenterX() / 2 + ): + zoomedCursorX = (unzoomedCursorX * 2.0) - daq_utils.screenPixX + if ( + unzoomedCursorY - self.getMD2BeamCenterY() + > self.getMD2BeamCenterY() / 2 + ): + zoomedCursorY = (unzoomedCursorY * 2.0) - daq_utils.screenPixY + self.centerMarker.setPos(zoomedCursorX, zoomedCursorY) + self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) + self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) + self.beamSizeOverlay.setRect( + self.overlayPosOffsetX + + self.centerMarker.x() + - (self.beamSizeXPixels / 2), + self.overlayPosOffsetY + + self.centerMarker.y() + - (self.beamSizeYPixels / 2), + self.beamSizeXPixels, + self.beamSizeYPixels, + ) + else: + self.centerMarker.setPos( + self.lowMagCursorX_pv.get() - self.centerMarkerCharOffsetX, + self.lowMagCursorY_pv.get() - self.centerMarkerCharOffsetY, + ) + self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) + self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) + self.beamSizeOverlay.setRect( + self.overlayPosOffsetX + + self.centerMarker.x() + - (self.beamSizeXPixels / 2), + self.overlayPosOffsetY + + self.centerMarker.y() + - (self.beamSizeYPixels / 2), + self.beamSizeXPixels, + self.beamSizeYPixels, + ) + + def processHighMagCursorChange(self, posRBV, ID): + zoomedCursorX = self.getMD2BeamCenterX() - self.centerMarkerCharOffsetX + zoomedCursorY = self.getMD2BeamCenterY() - self.centerMarkerCharOffsetY + if self.zoom4Radio.isChecked(): # highmagzoom + unzoomedCursorX = ( + self.highMagCursorX_pv.get() - self.centerMarkerCharOffsetX + ) + unzoomedCursorY = ( + self.highMagCursorY_pv.get() - self.centerMarkerCharOffsetY + ) + if unzoomedCursorX * 2.0 < self.getMD2BeamCenterX(): + zoomedCursorX = unzoomedCursorX * 2.0 + if unzoomedCursorY * 2.0 < self.getMD2BeamCenterY(): + zoomedCursorY = unzoomedCursorY * 2.0 + if ( + unzoomedCursorX - self.getMD2BeamCenterX() + > self.getMD2BeamCenterX() / 2 + ): + zoomedCursorX = (unzoomedCursorX * 2.0) - daq_utils.screenPixX + if ( + unzoomedCursorY - self.getMD2BeamCenterY() + > self.getMD2BeamCenterY() / 2 + ): + zoomedCursorY = (unzoomedCursorY * 2.0) - daq_utils.screenPixY + self.centerMarker.setPos(zoomedCursorX, zoomedCursorY) + self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) + self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) + self.beamSizeOverlay.setRect( + self.overlayPosOffsetX + + self.centerMarker.x() + - (self.beamSizeXPixels / 2), + self.overlayPosOffsetY + + self.centerMarker.y() + - (self.beamSizeYPixels / 2), + self.beamSizeXPixels, + self.beamSizeYPixels, + ) + else: + self.centerMarker.setPos( + self.highMagCursorX_pv.get() - self.centerMarkerCharOffsetX, + self.highMagCursorY_pv.get() - self.centerMarkerCharOffsetY, + ) + self.beamSizeXPixels = self.screenXmicrons2pixels(self.tempBeamSizeXMicrons) + self.beamSizeYPixels = self.screenYmicrons2pixels(self.tempBeamSizeYMicrons) + self.beamSizeOverlay.setRect( + self.overlayPosOffsetX + + self.centerMarker.x() + - (self.beamSizeXPixels / 2), + self.overlayPosOffsetY + + self.centerMarker.y() + - (self.beamSizeYPixels / 2), + self.beamSizeXPixels, + self.beamSizeYPixels, + ) + + def processSampMove(self, posRBV, motID): + # print "new " + motID + " pos=" + str(posRBV) + self.motPos[motID] = posRBV + if self.centeringMarksList: + for mark in self.centeringMarksList: + if mark is None: + continue + centerMarkerOffsetX = mark["centerCursorX"] - self.centerMarker.x() + centerMarkerOffsetY = mark["centerCursorY"] - self.centerMarker.y() + if motID == "x": + startX = mark["sampCoords"]["x"] + delta = startX - posRBV + newX = float(self.screenXmicrons2pixels(delta)) + mark["graphicsItem"].setPos( + newX - centerMarkerOffsetX, mark["graphicsItem"].y() + ) + if motID == "y" or motID == "z" or motID == "omega": + startYY = mark["sampCoords"]["z"] + startYX = mark["sampCoords"]["y"] + newY = self.calculateNewYCoordPos(startYX, startYY) + mark["graphicsItem"].setPos( + mark["graphicsItem"].x(), newY - centerMarkerOffsetY + ) + if self.rasterList: + for raster in self.rasterList: + if raster is None: + continue + startX = raster["coords"]["x"] + startYY = raster["coords"]["z"] + startYX = raster["coords"]["y"] + if motID == "x": + delta = startX - posRBV + newX = float(self.screenXmicrons2pixels(delta)) + raster["graphicsItem"].setPos(newX, raster["graphicsItem"].y()) + if motID == "y" or motID == "z": + newY = self.calculateNewYCoordPos(startYX, startYY) + raster["graphicsItem"].setPos(raster["graphicsItem"].x(), newY) + if motID == "fineX": + delta = startX - posRBV - self.motPos["x"] + newX = float(self.screenXmicrons2pixels(delta)) + raster["graphicsItem"].setPos(newX, raster["graphicsItem"].y()) + if motID == "fineY" or motID == "fineZ": + newY = self.calculateNewYCoordPos(startYX, startYY) + raster["graphicsItem"].setPos(raster["graphicsItem"].x(), newY) + if motID == "omega": + if abs(posRBV - raster["coords"]["omega"]) % 360.0 > 5.0: + raster["graphicsItem"].setVisible(False) + else: + raster["graphicsItem"].setVisible(True) + newY = self.calculateNewYCoordPos(startYX, startYY) + raster["graphicsItem"].setPos(raster["graphicsItem"].x(), newY) + + self.vectorStart = self.updatePoint(self.vectorStart, posRBV, motID) + self.vectorEnd = self.updatePoint(self.vectorEnd, posRBV, motID) + if self.vectorStart != None and self.vectorEnd != None: + self.vecLine.setLine( + self.vectorStart["graphicsitem"].x() + + self.vectorStart["centerCursorX"] + + self.centerMarkerCharOffsetX, + self.vectorStart["graphicsitem"].y() + + self.vectorStart["centerCursorY"] + + self.centerMarkerCharOffsetY, + self.vectorEnd["graphicsitem"].x() + + self.vectorStart["centerCursorX"] + + self.centerMarkerCharOffsetX, + self.vectorEnd["graphicsitem"].y() + + self.vectorStart["centerCursorY"] + + self.centerMarkerCharOffsetY, + ) + + def updatePoint(self, point, posRBV, motID): + """Updates a point on the screen + + Updates the position of a point (e.g. self.vectorStart) drawn on the screen based on + which motor was moved (motID) using gonio position (posRBV) + """ + if point is None: + return point + centerMarkerOffsetX = point["centerCursorX"] - self.centerMarker.x() + centerMarkerOffsetY = point["centerCursorY"] - self.centerMarker.y() + startYY = point["coords"]["z"] + startYX = point["coords"]["y"] + startX = point["coords"]["x"] + + if motID == "omega": + newY = self.calculateNewYCoordPos(startYX, startYY) + point["graphicsitem"].setPos( + point["graphicsitem"].x(), newY - centerMarkerOffsetY + ) + if motID == "x": + delta = startX - posRBV + newX = float(self.screenXmicrons2pixels(delta)) + point["graphicsitem"].setPos( + newX - centerMarkerOffsetX, point["graphicsitem"].y() + ) + if motID == "y" or motID == "z": + newY = self.calculateNewYCoordPos(startYX, startYY) + point["graphicsitem"].setPos( + point["graphicsitem"].x(), newY - centerMarkerOffsetY + ) + return point + + def queueEnScanCB(self): + self.protoComboBox.setCurrentIndex(self.protoComboBox.findText(str("eScan"))) + self.addRequestsToAllSelectedCB() + self.treeChanged_pv.put(1) + + def clearEnScanPlotCB(self): + self.EScanGraph.removeCurves() # get list of all curves to provide to method? + self.choochGraph.removeCurves() + + def displayXrecRaster(self, xrecRasterFlag): + self.xrecRasterFlag_pv.put("0") + if xrecRasterFlag == "100": + for i in range(len(self.rasterList)): + if self.rasterList[i] != None: + self.scene.removeItem(self.rasterList[i]["graphicsItem"]) + else: + logger.info("xrecrasterflag = %s" % xrecRasterFlag) + try: + rasterReq = db_lib.getRequestByID(xrecRasterFlag) + except IndexError: + logger.error("bad xrecRasterFlag: %s" % xrecRasterFlag) + return + rasterDef = rasterReq["request_obj"]["rasterDef"] + if rasterDef["status"] == RasterStatus.DRAWN.value: + self.drawPolyRaster(rasterReq) + elif rasterDef["status"] == RasterStatus.READY_FOR_FILL.value: + self.fillPolyRaster( + rasterReq, waitTime=getBlConfig(RASTER_GUI_XREC_FILL_DELAY) + ) + logger.info("polyraster filled by displayXrecRaster") + elif rasterDef["status"] == RasterStatus.READY_FOR_SNAPSHOT.value: + if self.controlEnabled(): + self.takeRasterSnapshot(rasterReq) + logger.info("raster snapshot taken") + self.vidActionRasterExploreRadio.setChecked(True) + self.selectedSampleID = rasterReq["sample"] + self.treeChanged_pv.put(1) # not sure about this + elif rasterDef["status"] == RasterStatus.READY_FOR_REPROCESS.value: + self.fillPolyRaster(rasterReq) + logger.info("reprocessed polyraster filled by displayXrecraster") + if self.controlEnabled(): + self.takeRasterSnapshot(rasterReq) + logger.info("reprocessed raster snapshot taken") + self.vidActionRasterExploreRadio.setChecked(True) + self.selectedSampleID = rasterReq["sample"] + self.treeChanged_pv.put(1) # not sure about this + else: + pass + + def processMountedPin(self, mountedPinPos): + self.eraseCB() + self.treeChanged_pv.put(1) + + def processFastShutter(self, shutterVal): + if round(shutterVal) == round(self.fastShutterOpenPos_pv.get()): + self.shutterStateLabel.setText("Shutter State:Open") + self.shutterStateLabel.setStyleSheet("background-color: red;") + else: + self.shutterStateLabel.setText("Shutter State:Closed") + self.shutterStateLabel.setStyleSheet("background-color: #99FF66;") + + def processGripTemp(self, gripVal): + self.gripperTempLabel.setText("%.1f" % gripVal) + if int(gripVal) > -170: + self.gripperTempLabel.setStyleSheet("background-color: red;") + else: + self.gripperTempLabel.setStyleSheet("background-color: #99FF66;") + + def processCryostreamTemp(self, cryostreamVal): + self.cryostreamTempLabel.setText(str(cryostreamVal)) + + def processRingCurrent(self, ringCurrentVal): + self.ringCurrentMessage.setText(str(int(ringCurrentVal))) + if int(ringCurrentVal) < 390: + self.ringCurrentMessage.setStyleSheet("background-color: red;") + else: + self.ringCurrentMessage.setStyleSheet("background-color: #99FF66;") + + def processBeamAvailable(self, beamAvailVal): + if int(beamAvailVal) == 1: + self.beamAvailLabel.setText("Beam Available") + self.beamAvailLabel.setStyleSheet("background-color: #99FF66;") + else: + self.beamAvailLabel.setText("No Beam") + self.beamAvailLabel.setStyleSheet("background-color: red;") + + def processSampleExposed(self, sampleExposedVal): + if int(sampleExposedVal) == 1: + self.sampleExposedLabel.setText("Sample Exposed") + self.sampleExposedLabel.setStyleSheet("background-color: red;") + else: + self.sampleExposedLabel.setText("Sample Not Exposed") + self.sampleExposedLabel.setStyleSheet("background-color: #99FF66;") + + def processBeamSize(self, beamSizeFlag): + self.beamsizeComboBox.setCurrentIndex(beamSizeFlag) + + def processEnergyChange(self, energyVal): + if daq_utils.beamline != "amx": + if energyVal < 9000: + self.beamsizeComboBox.setEnabled(False) + else: + self.beamsizeComboBox.setEnabled(True) + + def processControlMaster(self, controlPID): + logger.info("in callback controlPID = " + str(controlPID)) + if abs(int(controlPID)) == self.processID: + self.controlMasterCheckBox.setChecked(True) + else: + self.controlMasterCheckBox.setChecked(False) + + def processZebraArmState(self, state): + if int(state): + self.userScreenDialog.zebraArmCheckBox.setChecked(True) + else: + self.userScreenDialog.zebraArmCheckBox.setChecked(False) + + def processGovRobotSeReach(self, state): + if int(state): + self.userScreenDialog.SEbutton.setEnabled(True) + else: + self.userScreenDialog.SEbutton.setEnabled(False) + + def processGovRobotSaReach(self, state): + if int(state): + self.userScreenDialog.SAbutton.setEnabled(True) + else: + self.userScreenDialog.SAbutton.setEnabled(False) + + def processGovRobotDaReach(self, state): + if int(state): + self.userScreenDialog.DAbutton.setEnabled(True) + else: + self.userScreenDialog.DAbutton.setEnabled(False) + + def processGovRobotBlReach(self, state): + if int(state): + self.userScreenDialog.BLbutton.setEnabled(True) + else: + self.userScreenDialog.BLbutton.setEnabled(False) + + def processDetMessage(self, state): + self.userScreenDialog.detMessage_ledit.setText(str(state)) + + def processSampleFlux(self, state): + self.userScreenDialog.sampleFluxLabel.setText("%E" % state) + + def processZebraPulseState(self, state): + if int(state): + self.userScreenDialog.zebraPulseCheckBox.setChecked(True) + else: + self.userScreenDialog.zebraPulseCheckBox.setChecked(False) + + def processStillModeState(self, state): + if int(state): + self.stillModeCheckBox.setChecked(True) + else: + self.stillModeCheckBox.setChecked(False) + + def processZebraDownloadState(self, state): + if int(state): + self.userScreenDialog.zebraDownloadCheckBox.setChecked(True) + else: + self.userScreenDialog.zebraDownloadCheckBox.setChecked(False) + + def processZebraSentTriggerState(self, state): + if int(state): + self.userScreenDialog.zebraSentTriggerCheckBox.setChecked(True) + else: + self.userScreenDialog.zebraSentTriggerCheckBox.setChecked(False) + + def processZebraReturnedTriggerState(self, state): + if int(state): + self.userScreenDialog.zebraReturnedTriggerCheckBox.setChecked(True) + else: + self.userScreenDialog.zebraReturnedTriggerCheckBox.setChecked(False) + + def processControlMasterNew(self, controlPID): + logger.info("in callback controlPID = " + str(controlPID)) + if abs(int(controlPID)) != self.processID: + self.controlMasterCheckBox.setChecked(False) + + def processChoochResult(self, choochResultFlag): + if choochResultFlag == "0": + return + try: + choochResult = db_lib.getResult(choochResultFlag) + except IndexError: # if no result, just return as if no result + logger.info( + f"Exception while retrieving result for chooch result so not plotting but continuing GUI: {choochResultFlag}" + ) + return + choochResultObj = choochResult["result_obj"] + graph_x = choochResultObj["choochInXAxis"] + graph_y = choochResultObj["choochInYAxis"] + self.EScanGraph.name = "Chooch PLot" + try: + self.EScanGraph.addCurve(graph_x, graph_y, "Raw counts vs. energy") + self.EScanGraph.replot() + except TypeError as e: + logger.error( + "Problems with data type going into energy scan plot: %s" % (e) + ) + chooch_graph_x = choochResultObj["choochOutXAxis"] + chooch_graph_y1 = choochResultObj["choochOutY1Axis"] + chooch_graph_y2 = choochResultObj["choochOutY2Axis"] + self.choochGraph.name = "Chooch PLot" + try: + self.choochGraph.addCurve(chooch_graph_x, chooch_graph_y1, legend="spline") + self.choochGraph.addCurve(chooch_graph_x, chooch_graph_y2, legend="fp") + self.choochGraph.replot() + self.choochInfl.setText(str(choochResultObj["infl"])) + self.choochPeak.setText(str(choochResultObj["peak"])) + self.choochFPrimeInfl.setText(str(choochResultObj["fprime_infl"])) + self.choochFPrimePeak.setText(str(choochResultObj["fprime_peak"])) + self.choochF2PrimeInfl.setText(str(choochResultObj["f2prime_infl"])) + self.choochF2PrimePeak.setText(str(choochResultObj["f2prime_peak"])) + self.choochResultFlag_pv.put("0") + self.protoComboBox.setCurrentIndex( + self.protoComboBox.findText(str("standard")) + ) + self.protoComboActivatedCB("standard") + except TypeError as e: + logger.error( + "Chooch plotting failed - check whether scan had a strong signal or not: %s" + % (e) + ) + + # seems like we should be able to do an aggregate query to mongo for max/min :( + def getMaxPriority(self): + orderedRequests = db_lib.getOrderedRequestList(daq_utils.beamline) + priorityMax = 0 + for i in range(len(orderedRequests)): + if orderedRequests[i]["priority"] > priorityMax: + priorityMax = orderedRequests[i]["priority"] + return priorityMax + + def getMinPriority(self): + orderedRequests = db_lib.getOrderedRequestList(daq_utils.beamline) + priorityMin = 10000000 + for i in range(len(orderedRequests)): + if (orderedRequests[i]["priority"] < priorityMin) and orderedRequests[i][ + "priority" + ] > 0: + priorityMin = orderedRequests[i]["priority"] + return priorityMin + + def showProtParams(self): + protocol = str(self.protoComboBox.currentText()) + self.rasterParamsFrame.hide() + self.characterizeParamsFrame.hide() + self.processingOptionsFrame.hide() + self.multiColParamsFrame.hide() + self.osc_start_ledit.setEnabled(True) + self.osc_end_ledit.setEnabled(True) + if protocol == "raster" or protocol == "rasterScreen": + self.rasterParamsFrame.show() + self.osc_start_ledit.setEnabled(False) + self.osc_end_ledit.setEnabled(False) + + elif protocol == "stepRaster": + self.rasterParamsFrame.show() + self.processingOptionsFrame.show() + elif protocol == "multiCol" or protocol == "multiColQ": + self.rasterParamsFrame.show() + self.osc_start_ledit.setEnabled(False) + self.osc_end_ledit.setEnabled(False) + self.multiColParamsFrame.show() + elif protocol == "vector" or protocol == "stepVector": + self.vectorParamsFrame.show() + self.processingOptionsFrame.show() + elif protocol == "characterize" or protocol == "ednaCol": + self.characterizeParamsFrame.show() + self.processingOptionsFrame.show() + elif protocol == "standard" or protocol == "burn": + self.processingOptionsFrame.show() + else: + pass + + def rasterStepChanged(self, text): + self.beamWidth_ledit.setText(text) + self.beamHeight_ledit.setText(text) + + def updateVectorLengthAndSpeed(self): + x_vec_end = self.vectorEnd["coords"]["x"] + y_vec_end = self.vectorEnd["coords"]["y"] + z_vec_end = self.vectorEnd["coords"]["z"] + x_vec_start = self.vectorStart["coords"]["x"] + y_vec_start = self.vectorStart["coords"]["y"] + z_vec_start = self.vectorStart["coords"]["z"] + x_vec = x_vec_end - x_vec_start + y_vec = y_vec_end - y_vec_start + z_vec = z_vec_end - z_vec_start + trans_total = math.sqrt(x_vec**2 + y_vec**2 + z_vec**2) + if daq_utils.beamline == "nyx": + trans_total *= 1000 + self.vecLenLabelOutput.setText(str(int(trans_total))) + totalExpTime = ( + float(self.osc_end_ledit.text()) / float(self.osc_range_ledit.text()) + ) * float( + self.exp_time_ledit.text() + ) # (range/inc)*exptime + speed = trans_total / totalExpTime + self.vecSpeedLabelOutput.setText(str(int(speed))) + return x_vec, y_vec, z_vec, trans_total + + def totalExpChanged(self, text): + if text == "oscEnd" and daq_utils.beamline == "fmx": + self.sampleLifetimeReadback_ledit.setStyleSheet("color : gray") + try: + if float(str(self.osc_range_ledit.text())) == 0: + if text == "oscRange": + if self.controlEnabled(): + self.stillMode_pv.put(1) + self.colEndLabel.setText("Number of Images: ") + if ( + str(self.protoComboBox.currentText()) != "standard" + and str(self.protoComboBox.currentText()) != "vector" + ): + self.totalExptime_ledit.setText("----") + else: + try: + totalExptime = float(self.osc_end_ledit.text()) * float( + self.exp_time_ledit.text() + ) + except ValueError: + totalExptime = 0.0 + except TypeError: + totalExptime = 0.0 + except ZeroDivisionError: + totalExptime = 0.0 + self.totalExptime_ledit.setText("%.3f" % totalExptime) + return + else: + if text == "oscRange": + if self.controlEnabled(): + self.standardMode_pv.put(1) + self.colEndLabel.setText("Oscillation Range:") + except ValueError: + return + + if ( + str(self.protoComboBox.currentText()) != "standard" + and str(self.protoComboBox.currentText()) != "vector" + ): + self.totalExptime_ledit.setText("----") + else: + try: + totalExptime = ( + float(self.osc_end_ledit.text()) + / (float(self.osc_range_ledit.text())) + ) * float(self.exp_time_ledit.text()) + except ValueError: + totalExptime = 0.0 + except TypeError: + totalExptime = 0.0 + except ZeroDivisionError: + totalExptime = 0.0 + self.totalExptime_ledit.setText("%.3f" % totalExptime) + if str(self.protoComboBox.currentText()) == "vector": + try: + self.updateVectorLengthAndSpeed() + except: + pass + + try: + if float(self.osc_end_ledit.text()) >= 5.0: + self.staffScreenDialog.fastDPCheckBox.setChecked(True) + else: + self.staffScreenDialog.fastDPCheckBox.setChecked(False) + except: + pass + + def resoTextChanged(self, text): + try: + dist_s = "%.2f" % ( + daq_utils.distance_from_reso( + daq_utils.det_radius, + float(text), + daq_utils.energy2wave(float(self.energy_ledit.text())), + 0, + ) + ) + except ValueError: + dist_s = self.detDistRBVLabel.getEntry().text() + self.detDistMotorEntry.getEntry().setText(dist_s) + + def detDistTextChanged(self, text): + try: + reso_s = "%.2f" % ( + daq_utils.calc_reso( + daq_utils.det_radius, + float(text), + daq_utils.energy2wave(float(self.energy_ledit.text())), + 0, + ) + ) + except ValueError: + reso_s = "50.0" + except TypeError: + reso_s = "50.0" + self.setGuiValues({"resolution": reso_s}) + + def energyTextChanged(self, text): + dist_s = "%.2f" % ( + daq_utils.distance_from_reso( + daq_utils.det_radius, + float(self.resolution_ledit.text()), + float(text), + 0, + ) + ) + self.detDistMotorEntry.getEntry().setText(dist_s) + + # code below and its application from: https://snorfalorpagus.net/blog/2014/08/09/validating-user-input-in-pyqt4-using-qvalidator/ + def checkEntryState(self, *args, **kwargs): + sender = self.sender() + validator = sender.validator() + state = validator.validate(sender.text(), 0)[0] + if state == QtGui.QValidator.Intermediate: + color = "#fff79a" # yellow + elif state == QtGui.QValidator.Invalid: + color = "#f6989d" # red + else: + color = "#ffffff" # white + sender.setStyleSheet("QLineEdit { background-color: %s }" % color) + + def validateAllFields(self): + fields_dict = { + self.exp_time_ledit: {"name": "exposure time", "minmax": VALID_EXP_TIMES}, + self.detDistMotorEntry.getEntry(): { + "name": "detector distance", + "minmax": VALID_DET_DIST, + }, + self.totalExptime_ledit: { + "name": "total exposure time", + "minmax": VALID_TOTAL_EXP_TIMES, + }, + } + + return self.validateFields(fields_dict) + + def validateFields(self, field_values_dict): + for field, value in field_values_dict.items(): + values = value["minmax"] + field_name = value["name"] + logger.info("validateFields: %s %s %s" % (field_name, field.text(), values)) + try: + val = float(field.text()) + logger.info( + ">= min: %s <= max: %s" + % (val >= values["fmx"]["min"], val <= values["fmx"]["max"]) + ) + except: # total exposure time is '----' for rasters, so just ignore + pass + if ( + field.text() == "----" + ): # special case: total exp time not calculated for non-standard, non-vector experiments + continue + if ( + field.validator().validate(field.text(), 0)[0] + != QtGui.QValidator.Acceptable + ): + self.popupServerMessage( + "Invalid value for field %s! must be between %s and %s" + % ( + field_name, + values[daq_utils.beamline]["min"], + values[daq_utils.beamline]["max"], + ) + ) + return False + return True + + def protoRadioToggledCB(self, text): + if self.protoStandardRadio.isChecked(): + self.protoComboBox.setCurrentIndex(self.protoComboBox.findText("standard")) + self.protoComboActivatedCB(text) + elif self.protoRasterRadio.isChecked(): + self.protoComboBox.setCurrentIndex(self.protoComboBox.findText("raster")) + self.protoComboActivatedCB(text) + elif self.protoVectorRadio.isChecked(): + self.protoComboBox.setCurrentIndex(self.protoComboBox.findText("vector")) + self.protoComboActivatedCB(text) + else: + pass + + def beamsizeComboActivatedCB(self, text): + if daq_utils.beamline == "nyx": + index = self.beamsizeComboBox.findText(str(text)) + self.aperture.current_index.put(index) + else: + comm_s = 'set_beamsize("' + str(text[0:2]) + '","' + str(text[2:4]) + '")' + logger.info(comm_s) + self.send_to_server(comm_s) + + def protoComboActivatedCB(self, text): + self.showProtParams() + protocol = str(self.protoComboBox.currentText()) + if protocol in ("raster", "stepRaster", "rasterScreen", "multiCol"): + self.vidActionRasterDefRadio.setChecked(True) + else: + self.vidActionC2CRadio.setChecked(True) + if protocol == "burn": + self.staffScreenDialog.fastDPCheckBox.setChecked(False) + else: + self.staffScreenDialog.fastDPCheckBox.setChecked(True) + if protocol == "raster": + self.protoRasterRadio.setChecked(True) + self.osc_start_ledit.setEnabled(False) + self.osc_end_ledit.setEnabled(False) + self.setGuiValues( + { + "osc_range": getBlConfig("rasterDefaultWidth"), + "exp_time": getBlConfig("rasterDefaultTime"), + "transmission": getBlConfig("rasterDefaultTrans"), + } + ) + elif protocol == "rasterScreen": + self.osc_start_ledit.setEnabled(False) + self.osc_end_ledit.setEnabled(False) + self.setGuiValues( + { + "osc_range": getBlConfig("rasterDefaultWidth"), + "exp_time": getBlConfig("rasterDefaultTime"), + "transmission": getBlConfig("rasterDefaultTrans"), + } + ) + self.protoOtherRadio.setChecked(True) + elif protocol == "standard": + self.protoStandardRadio.setChecked(True) + self.setGuiValues( + { + "osc_range": getBlConfig("screen_default_width"), + "exp_time": getBlConfig("screen_default_time"), + "transmission": getBlConfig("stdTrans"), + } + ) + self.osc_start_ledit.setEnabled(True) + self.osc_end_ledit.setEnabled(True) + elif protocol == "burn": + self.setGuiValues( + { + "osc_range": "0.0", + "exp_time": getBlConfig("burnDefaultTime"), + "transmission": getBlConfig("burnDefaultTrans"), + } + ) + screenWidth = float(getBlConfig("burnDefaultNumFrames")) + self.setGuiValues({"osc_end": screenWidth}) + self.osc_start_ledit.setEnabled(True) + self.osc_end_ledit.setEnabled(True) + + elif protocol == "vector": + self.setGuiValues( + { + "osc_range": getBlConfig("screen_default_width"), + "exp_time": getBlConfig("screen_default_time"), + "transmission": getBlConfig("stdTrans"), + } + ) + self.osc_start_ledit.setEnabled(True) + self.osc_end_ledit.setEnabled(True) + self.protoVectorRadio.setChecked(True) + else: + self.protoOtherRadio.setChecked(True) + self.totalExpChanged("") + + def rasterEvalComboActivatedCB(self, text): + db_lib.beamlineInfo( + daq_utils.beamline, + "rasterScoreFlag", + info_dict={"index": self.rasterEvalComboBox.findText(str(text))}, + ) + if self.currentRasterCellList != []: + self.reFillPolyRaster() + + def popBaseDirectoryDialogCB(self): + fname = QtWidgets.QFileDialog.getExistingDirectory( + self, "Choose Directory", "", QtWidgets.QFileDialog.DontUseNativeDialog + ) + if fname != "": + self.dataPathGB.setBasePath_ledit(fname) + + def popImportDialogCB(self): + #self.timerSample.stop() + fname = QtWidgets.QFileDialog.getOpenFileName( + self, + "Choose Spreadsheet File", + "", + filter="*.xls *.xlsx", + options=QtWidgets.QFileDialog.DontUseNativeDialog, + ) + #self.timerSample.start(SAMPLE_TIMER_DELAY) + if fname != "": + logger.info(fname) + comm_s = f'importSpreadsheet("{fname[0]}", "{daq_utils.owner}")' + logger.info(comm_s) + self.send_to_server(comm_s) + + def setUserModeCB(self): + self.vidActionDefineCenterRadio.setEnabled(False) + + def setExpertModeCB(self): + self.vidActionDefineCenterRadio.setEnabled(True) + + def upPriorityCB( + self, + ): # neither of these are very elegant, and might even be glitchy if overused + currentPriority = self.selectedSampleRequest["priority"] + if currentPriority < 1: + return + orderedRequests = db_lib.getOrderedRequestList(daq_utils.beamline) + for i in range(len(orderedRequests)): + if orderedRequests[i]["sample"] == self.selectedSampleRequest["sample"]: + if i < 2: + self.topPriorityCB() + else: + priority = ( + orderedRequests[i - 2]["priority"] + + orderedRequests[i - 1]["priority"] + ) / 2 + if currentPriority == priority: + priority = priority + 20 + db_lib.updatePriority(self.selectedSampleRequest["uid"], priority) + self.treeChanged_pv.put(1) + + def downPriorityCB(self): + currentPriority = self.selectedSampleRequest["priority"] + if currentPriority < 1: + return + orderedRequests = db_lib.getOrderedRequestList(daq_utils.beamline) + for i in range(len(orderedRequests)): + if orderedRequests[i]["sample"] == self.selectedSampleRequest["sample"]: + if (len(orderedRequests) - i) < 3: + self.bottomPriorityCB() + else: + priority = ( + orderedRequests[i + 1]["priority"] + + orderedRequests[i + 2]["priority"] + ) / 2 + if currentPriority == priority: + priority = priority - 20 + db_lib.updatePriority(self.selectedSampleRequest["uid"], priority) + self.treeChanged_pv.put(1) + + def topPriorityCB(self): + currentPriority = self.selectedSampleRequest["priority"] + if currentPriority < 1: + return + priority = int(self.getMaxPriority()) + priority = priority + 100 + db_lib.updatePriority(self.selectedSampleRequest["uid"], priority) + self.treeChanged_pv.put(1) + + def bottomPriorityCB(self): + currentPriority = self.selectedSampleRequest["priority"] + if currentPriority < 1: + return + priority = int(self.getMinPriority()) + priority = priority - 100 + db_lib.updatePriority(self.selectedSampleRequest["uid"], priority) + self.treeChanged_pv.put(1) + + def dewarViewToggledCB(self, identifier): + self.selectedSampleRequest = {} + # should probably clear textfields here too + if identifier == "dewarView": + if self.dewarViewRadio.isChecked(): + self.dewarTree.refreshTreeDewarView() + else: + if self.priorityViewRadio.isChecked(): + self.dewarTree.refreshTreePriorityView() + + def dewarViewToggleCheckCB(self): + if self.dewarViewRadio.isChecked(): + self.dewarTree.refreshTreeDewarView() + else: + self.dewarTree.refreshTreePriorityView() + + def moveOmegaCB(self): + comm_s = ( + 'omegaMoveAbs(' + + str(self.sampleOmegaMoveLedit.getEntry().text()) + + ")" + ) + logger.info(comm_s) + self.send_to_server(comm_s) + + def moveEnergyCB(self): + energyRequest = float(str(self.energy_ledit.text())) + if abs(energyRequest - self.energy_pv.get()) > 10.0: + self.popupServerMessage("Energy change must be less than 10 ev") + return + else: + comm_s = 'mvaDescriptor("energy",' + str(self.energy_ledit.text()) + ")" + logger.info(comm_s) + self.send_to_server(comm_s) + + def setLifetimeCB(self, lifetime): + if hasattr(self, "sampleLifetimeReadback_ledit"): + self.sampleLifetimeReadback_ledit.setText(f"{lifetime:.2f}") + self.sampleLifetimeReadback_ledit.setStyleSheet("color : black") + + def calcLifetimeCB(self): + if not os.path.exists("2vb1.pdb"): + os.system("cp -a $CONFIGDIR/2vb1.pdb .") + os.system("mkdir rd3d") + + energyReadback = self.energy_pv.get() / 1000.0 + sampleFlux = self.sampleFluxPV.get() + if hasattr(self, "transmission_ledit") and hasattr( + self, "transmissionReadback_ledit" + ): + try: + sampleFlux = ( + sampleFlux * float(self.transmission_ledit.text()) + ) / float(self.transmissionReadback_ledit.text()) + except Exception as e: + logger.info(f"Exception while calculating sample flux {e}") + logger.info("sample flux = " + str(sampleFlux)) + try: + vecLen_s = self.vecLenLabelOutput.text() + if vecLen_s != "---": + vecLen = float(vecLen_s) + else: + vecLen = 0 + except: + vecLen = 0 + wedge = float(self.osc_end_ledit.text()) + try: + raddose_thread = RaddoseThread( + parent=self, + beamsizeV=3.0, + beamsizeH=5.0, + vectorL=vecLen, + energy=energyReadback, + wedge=wedge, + flux=sampleFlux, + verbose=True, + ) + raddose_thread.lifetime.connect( + lambda lifetime: self.setLifetimeCB(lifetime) + ) + raddose_thread.start() + + except: + lifeTime_s = "0.00" + + def setTransCB(self): + try: + if ( + float(self.transmission_ledit.text()) > 1.0 + or float(self.transmission_ledit.text()) < 0.001 + ): + self.popupServerMessage("Transmission must be 0.001-1.0") + return + except ValueError as e: + self.popupServerMessage("Please enter a valid number") + return + comm_s = "setTrans(" + str(self.transmission_ledit.text()) + ")" + logger.info(comm_s) + self.send_to_server(comm_s) + + def setDCStartCB(self): + currentPos = float(self.sampleOmegaRBVLedit.getEntry().text()) % 360.0 + self.setGuiValues({"osc_start": currentPos}) + + def moveDetDistCB(self): + comm_s = ( + 'mvaDescriptor("detectorDist",' + + str(self.detDistMotorEntry.getEntry().text()) + + ")" + ) + logger.info(comm_s) + self.send_to_server(comm_s) + + def omegaTweakNegCB(self): + tv = float(self.omegaTweakVal_ledit.text()) + tweakVal = 0.0 - tv + if self.controlEnabled(): + mv_status = self.gon.omega.move(self.gon.omega.val() + tweakVal) + else: + self.popupServerMessage("You don't have control") + + def omegaTweakPosCB(self): + tv = float(self.omegaTweakVal_ledit.text()) + if self.controlEnabled(): + mv_status = self.gon.omega.move(self.gon.omega.val() + tv) + else: + self.popupServerMessage("You don't have control") + + def focusTweakCB(self, tv): + tvf = float(tv) * daq_utils.unitScaling + + current_viewangle = daq_utils.mag1ViewAngle + if self.zoom2Radio.isChecked(): + current_viewangle = daq_utils.mag2ViewAngle + elif self.zoom3Radio.isChecked(): + current_viewangle = daq_utils.mag3ViewAngle + elif self.zoom4Radio.isChecked(): + current_viewangle = daq_utils.mag4ViewAngle + + if current_viewangle == daq_utils.CAMERA_ANGLE_BEAM: + view_omega_offset = 0 + elif current_viewangle == daq_utils.CAMERA_ANGLE_BELOW: + view_omega_offset = 90 + elif current_viewangle == daq_utils.CAMERA_ANGLE_ABOVE: + view_omega_offset = -90 + + if self.controlEnabled(): + tvY = tvf * ( + math.cos(math.radians(view_omega_offset + self.gon.omega.val())) + ) # these are opposite C2C + tvZ = tvf * ( + math.sin(math.radians(view_omega_offset + self.gon.omega.val())) + ) + self.gon.y.move(self.gon.y.val() + tvY) + self.gon.z.move(self.gon.z.val() + tvZ) + else: + self.popupServerMessage("You don't have control") + + def omegaTweakCB(self, tv): + tvf = float(tv) + if self.controlEnabled(): + status = self.gon.omega.move(self.gon.omega.val() + tvf) + status.wait() + else: + self.popupServerMessage("You don't have control") + + def autoCenterLoopCB(self): + logger.info("auto center loop") + self.send_to_server("loop_center_xrec()") + + def autoRasterLoopCB(self): + self.selectedSampleID = self.selectedSampleRequest["sample"] + comm_s = "autoRasterLoop(" + str(self.selectedSampleID) + ")" + self.send_to_server(comm_s) + + def runRastersCB(self): + comm_s = "snakeRaster(" + str(self.selectedSampleRequest["uid"]) + ")" + self.send_to_server(comm_s) + + def drawInteractiveRasterCB(self): # any polygon for now, interactive or from xrec + for i in range(len(self.polyPointItems)): + self.scene.removeItem(self.polyPointItems[i]) + polyPointItems = [] + pen = QtGui.QPen(QtCore.Qt.red) + brush = QtGui.QBrush(QtCore.Qt.red) + points = [] + polyPoints = [] + if self.click_positions != []: # use the user clicks + if len(self.click_positions) == 2: # draws a single row or column + logger.info("2-click raster") + polyPoints.append(self.click_positions[0]) + point = QtCore.QPointF( + self.click_positions[0].x(), self.click_positions[1].y() + ) + polyPoints.append(point) + point = QtCore.QPointF( + self.click_positions[0].x() + 2, self.click_positions[1].y() + ) + polyPoints.append(point) + point = QtCore.QPointF( + self.click_positions[0].x() + 2, self.click_positions[0].y() + ) + polyPoints.append(point) + self.rasterPoly = QtWidgets.QGraphicsPolygonItem( + QtGui.QPolygonF(polyPoints) + ) + else: + self.rasterPoly = QtWidgets.QGraphicsPolygonItem( + QtGui.QPolygonF(self.click_positions) + ) + else: + return + self.polyBoundingRect = self.rasterPoly.boundingRect() + raster_w = int(self.polyBoundingRect.width()) + raster_h = int(self.polyBoundingRect.height()) + center_x = int(self.polyBoundingRect.center().x()) + center_y = int(self.polyBoundingRect.center().y()) + stepsizeXPix = self.screenXmicrons2pixels(float(self.rasterStepEdit.text())) + stepsizeYPix = self.screenYmicrons2pixels(float(self.rasterStepEdit.text())) + self.click_positions = [] + self.definePolyRaster( + raster_w, raster_h, stepsizeXPix, stepsizeYPix, center_x, center_y + ) + + def measurePolyCB(self): + for i in range(len(self.polyPointItems)): + self.scene.removeItem(self.polyPointItems[i]) + if self.measureLine != None: + self.scene.removeItem(self.measureLine) + self.polyPointItems = [] + + pen = QtGui.QPen(QtCore.Qt.red) + brush = QtGui.QBrush(QtCore.Qt.red) + points = [] + if self.click_positions != []: # use the user clicks + if len(self.click_positions) == 2: # draws a single row or column + self.measureLine = self.scene.addLine( + self.click_positions[0].x(), + self.click_positions[0].y(), + self.click_positions[1].x(), + self.click_positions[1].y(), + pen, + ) + length = self.measureLine.line().length() + fov = self.getCurrentFOV() + lineMicronsX = int(round(length * (fov["x"] / daq_utils.screenPixX))) + logger.info("linelength = " + str(lineMicronsX)) + self.click_positions = [] + + def center3LoopCB(self): + logger.info("3-click center loop") + self.threeClickCount = 1 + self.click3Button.setStyleSheet("background-color: yellow") + if(daq_utils.exporter_enabled): + self.md2.exporter.cmd("startManualSampleCentring", "") + else: + self.send_to_server('mvaDescriptor("omega",0)') + + def fillPolyRaster( + self, rasterReq, waitTime=1 + ): # at this point I should have a drawn polyRaster + time.sleep(waitTime) + logger.info("filling poly for " + str(rasterReq["uid"])) + resultCount = len(db_lib.getResultsforRequest(rasterReq["uid"])) + rasterResults = db_lib.getResultsforRequest(rasterReq["uid"]) + rasterResult = {} + for i in range(0, len(rasterResults)): + if rasterResults[i]["result_type"] == "rasterResult": + rasterResult = rasterResults[i] + break + try: + rasterDef = rasterReq["request_obj"]["rasterDef"] + except KeyError: + db_lib.deleteRequest(rasterReq["uid"]) + return + rasterListIndex = 0 + for i in range(len(self.rasterList)): + if self.rasterList[i] != None: + if self.rasterList[i]["uid"] == rasterReq["uid"]: + rasterListIndex = i + if rasterResult == {}: + return + + try: + currentRasterGroup = self.rasterList[rasterListIndex]["graphicsItem"] + except IndexError as e: + logger.error("IndexError while getting raster group: %s" % e) + return + self.currentRasterCellList = currentRasterGroup.childItems() + cellResults = rasterResult["result_obj"]["rasterCellResults"]["resultObj"] + numLines = len(cellResults) + cellResults_array = [{} for i in range(numLines)] + my_array = np.zeros(numLines) + spotLineCounter = 0 + cellIndex = 0 + rowStartIndex = 0 + rasterEvalOption = str(self.rasterEvalComboBox.currentText()) + lenX = abs( + rasterDef["rowDefs"][0]["end"]["x"] - rasterDef["rowDefs"][0]["start"]["x"] + ) # ugly for tile flip/noflip + for i in range( + len(rasterDef["rowDefs"]) + ): # this is building up "my_array" with the rasterEvalOption result, and numpy can then be run against the array. 2/16, I think cellResultsArray not needed + rowStartIndex = spotLineCounter + numsteps = rasterDef["rowDefs"][i]["numsteps"] + for j in range(numsteps): + try: + cellResult = cellResults[spotLineCounter] + except IndexError: + logger.error("caught index error #1") + logger.error("numlines = " + str(numLines)) + logger.error( + "expected: " + str(len(rasterDef["rowDefs"]) * numsteps) + ) + return # means a raster failure, and not enough data to cover raster, caused a gui crash + try: + spotcount = cellResult["spot_count_no_ice"] + filename = cellResult["image"] + except TypeError: + spotcount = 0 + filename = "empty" + + if ( + lenX > 180 and self.scannerType == "PI" + ): # this is trying to figure out row direction + cellIndex = spotLineCounter + else: + if i % 2 == 0: # this is trying to figure out row direction + cellIndex = spotLineCounter + else: + cellIndex = rowStartIndex + ((numsteps - 1) - j) + try: + if rasterEvalOption == "Spot Count": + my_array[cellIndex] = spotcount + elif rasterEvalOption == "Intensity": + my_array[cellIndex] = cellResult["total_intensity"] + else: + if float(cellResult["d_min"]) == -1: + my_array[cellIndex] = 50.0 + else: + my_array[cellIndex] = float(cellResult["d_min"]) + except IndexError: + logger.error("caught index error #2") + logger.error("numlines = " + str(numLines)) + logger.error( + "expected: " + str(len(rasterDef["rowDefs"]) * numsteps) + ) + return # means a raster failure, and not enough data to cover raster, caused a gui crash + cellResults_array[ + cellIndex + ] = cellResult # instead of just grabbing filename, get everything. Not sure why I'm building my own list of results. How is this different from cellResults? + # I don't think cellResults_array is different from cellResults, could maybe test that below by subtituting one for the other. It may be a remnant of trying to store less than the whole result set. + spotLineCounter += 1 + floor = np.amin(my_array) + ceiling = np.amax(my_array) + cellCounter = 0 + for i in range(len(rasterDef["rowDefs"])): + rowCellCount = 0 + for j in range(rasterDef["rowDefs"][i]["numsteps"]): + cellResult = cellResults_array[cellCounter] + try: + spotcount = int(cellResult["spot_count_no_ice"]) + cellFilename = cellResult["image"] + d_min = float(cellResult["d_min"]) + if d_min == -1: + d_min = 50.0 # trying to handle frames with no spots + total_intensity = int(cellResult["total_intensity"]) + except TypeError: + spotcount = 0 + cellFilename = "empty" + d_min = 50.0 + total_intensity = 0 + + if rasterEvalOption == "Spot Count": + param = spotcount + elif rasterEvalOption == "Intensity": + param = total_intensity + else: + param = d_min + if ceiling == 0: + color_id = 255 + elif ceiling == floor: + if rasterEvalOption == "Resolution": + color_id = 0 + else: + color_id = 255 + elif rasterEvalOption == "Resolution": + color_id = int( + 255.0 * (float(param - floor) / float(ceiling - floor)) + ) + else: + color_id = int( + 255 - (255.0 * (float(param - floor) / float(ceiling - floor))) + ) + self.currentRasterCellList[cellCounter].setBrush( + QtGui.QBrush(QtGui.QColor(0, 255 - color_id, 0, 127)) + ) + self.currentRasterCellList[cellCounter].setData(0, spotcount) + self.currentRasterCellList[cellCounter].setData(1, cellFilename) + self.currentRasterCellList[cellCounter].setData(2, d_min) + self.currentRasterCellList[cellCounter].setData(3, total_intensity) + cellCounter += 1 + + def takeRasterSnapshot(self, rasterReq): + request_obj = rasterReq["request_obj"] + directory = request_obj["directory"] + filePrefix = request_obj["file_prefix"] + basePath = request_obj["basePath"] + visitName = daq_utils.getVisitName() + jpegDirectory = ( + visitName + + "/jpegs/" + + directory[directory.find(visitName) + len(visitName) : len(directory)] + ) + fullJpegDirectory = basePath + "/" + jpegDirectory + if not os.path.exists(fullJpegDirectory): + os.system("mkdir -p " + fullJpegDirectory) + jpegImagePrefix = fullJpegDirectory + "/" + filePrefix + jpegImageFilename = jpegImagePrefix + ".jpg" + jpegImageThumbFilename = jpegImagePrefix + "t.jpg" + logger.info("saving raster snapshot") + self.saveVidSnapshotCB( + "Raster Result from sample " + str(rasterReq["request_obj"]["file_prefix"]), + useOlog=False, + reqID=rasterReq["uid"], + rasterHeatJpeg=jpegImageFilename, + ) + self.saveVidSnapshotCB( + "Raster Result from sample " + str(rasterReq["request_obj"]["file_prefix"]), + useOlog=False, + reqID=rasterReq["uid"], + rasterHeatJpeg=jpegImageFilename, + ) + try: + ispybLib.insertRasterResult(rasterReq, visitName) + except Exception as e: + logger.error(f"Exception while writing raster result: {e}") + + def reFillPolyRaster(self): + rasterEvalOption = str(self.rasterEvalComboBox.currentText()) + for i in range(len(self.rasterList)): + if self.rasterList[i] != None: + currentRasterGroup = self.rasterList[i]["graphicsItem"] + currentRasterCellList = currentRasterGroup.childItems() + my_array = np.zeros(len(currentRasterCellList)) + for i in range( + 0, len(currentRasterCellList) + ): # first loop is to get floor and ceiling + cellIndex = i + if rasterEvalOption == "Spot Count": + spotcount = currentRasterCellList[i].data(0) + if not isinstance(spotcount, int): + spotcount = int(spotcount) + my_array[cellIndex] = spotcount + elif rasterEvalOption == "Intensity": + total_intensity = currentRasterCellList[i].data(3) + if not isinstance(total_intensity, int): + total_intensity = int(total_intensity) + my_array[cellIndex] = total_intensity + else: + d_min = currentRasterCellList[i].data(2) + if not isinstance(d_min, float): + d_min = float(d_min) + if d_min == -1: + d_min = 50.0 # trying to handle frames with no spots + my_array[cellIndex] = d_min + floor = np.amin(my_array) + ceiling = np.amax(my_array) + for i in range(0, len(currentRasterCellList)): + if (rasterEvalOption == "Spot Count") or ( + rasterEvalOption == "Intensity" + ): + param = my_array[i] + else: + d_min = my_array[i] + if d_min == -1: + d_min = 50.0 # trying to handle frames with no spots + param = d_min + if ceiling == 0: + color_id = 255 + elif ceiling == floor: + if rasterEvalOption == "Resolution": + color_id = 0 + else: + color_id = 255 + elif rasterEvalOption == "Resolution": + color_id = int( + 255.0 * (float(param - floor) / float(ceiling - floor)) + ) + else: + color_id = int( + 255 + - (255.0 * (float(param - floor) / float(ceiling - floor))) + ) + currentRasterCellList[i].setBrush( + QtGui.QBrush(QtGui.QColor(0, 255 - color_id, 0, 127)) + ) + + def saveCenterCB(self): + pen = QtGui.QPen(QtCore.Qt.magenta) + brush = QtGui.QBrush(QtCore.Qt.magenta) + markWidth = 10 + marker = self.scene.addEllipse( + self.centerMarker.x() + - (markWidth / 2.0) + - 1 + + self.centerMarkerCharOffsetX, + self.centerMarker.y() + - (markWidth / 2.0) + - 1 + + self.centerMarkerCharOffsetY, + markWidth, + markWidth, + pen, + brush, + ) + marker.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True) + self.centeringMark = { + "sampCoords": { + "x": self.gon.x.val(), + "y": self.gon.y.val(), + "z": self.gon.z.val(), + }, + "graphicsItem": marker, + "centerCursorX": self.centerMarker.x(), + "centerCursorY": self.centerMarker.y(), + } + self.centeringMarksList.append(self.centeringMark) + + def selectAllCenterCB(self): + logger.info("select all center") + for i in range(len(self.centeringMarksList)): + self.centeringMarksList[i]["graphicsItem"].setSelected(True) + + def lightUpCB(self): + self.send_to_server("backlightBrighter()") + + def lightDimCB(self): + self.send_to_server("backlightDimmer()") + + def eraseRastersCB(self): + if self.rasterList != []: + for i in range(len(self.rasterList)): + if self.rasterList[i] != None: + self.scene.removeItem(self.rasterList[i]["graphicsItem"]) + self.rasterList = [] + self.rasterDefList = [] + self.currentRasterCellList = [] + + def eraseCB(self): + self.click_positions = [] + if self.measureLine != None: + self.scene.removeItem(self.measureLine) + for i in range(len(self.centeringMarksList)): + self.scene.removeItem(self.centeringMarksList[i]["graphicsItem"]) + self.centeringMarksList = [] + for i in range(len(self.polyPointItems)): + self.scene.removeItem(self.polyPointItems[i]) + self.polyPointItems = [] + if self.rasterList != []: + for i in range(len(self.rasterList)): + if self.rasterList[i] != None: + self.scene.removeItem(self.rasterList[i]["graphicsItem"]) + self.rasterList = [] + self.rasterDefList = [] + self.currentRasterCellList = [] + self.clearVectorCB() + if self.rasterPoly != None: + self.scene.removeItem(self.rasterPoly) + self.rasterPoly = None + + def eraseDisplayCB( + self, + ): # use this for things like zoom change. This is not the same as getting rid of all rasters. + if self.rasterList != []: + for i in range(len(self.rasterList)): + if self.rasterList[i] != None: + self.scene.removeItem(self.rasterList[i]["graphicsItem"]) + self.rasterList = [] + return # short circuit + if self.rasterPoly != None: + self.scene.removeItem(self.rasterPoly) + + def getCurrentFOV(self): + fov = {"x": 0.0, "y": 0.0} + if self.zoom2Radio.isChecked(): # lowmagzoom + if ( + daq_utils.sampleCameraCount == 2 + ): # this is a hard assumption that when there are 2 cameras the second uses highmagfov + fov["x"] = daq_utils.highMagFOVx + fov["y"] = daq_utils.highMagFOVy + else: + fov["x"] = daq_utils.lowMagFOVx / 2.0 + fov["y"] = daq_utils.lowMagFOVy / 2.0 + elif self.zoom1Radio.isChecked(): + fov["x"] = daq_utils.lowMagFOVx + fov["y"] = daq_utils.lowMagFOVy + elif self.zoom4Radio.isChecked(): + fov["x"] = daq_utils.highMagFOVx / 2.0 + fov["y"] = daq_utils.highMagFOVy / 2.0 + else: + fov["x"] = daq_utils.highMagFOVx + fov["y"] = daq_utils.highMagFOVy + return fov + + def screenXPixels2microns(self, pixels): + img_scale_factor = self.getMD2ImageXRatio() + pixels_per_mm = 1 / self.camera.scale_x.get() + pixels_per_micron = pixels_per_mm / 1000.0 + return float(pixels * img_scale_factor) / pixels_per_micron + print(f"pixels per micron = {pixels_per_micron}") + + def screenYPixels2microns(self, pixels): + pixels_per_mm = 1 / self.camera.scale_y.get() + pixels_per_micron = pixels_per_mm / 1000.0 + img_scale_factor = self.getMD2ImageYRatio() + return float(pixels * img_scale_factor) / pixels_per_micron + + def screenXmicrons2pixels(self, microns): + pixels_per_mm = 1 / self.camera.scale_x.get() + pixels_per_micron = pixels_per_mm / 1000.0 + img_scale_factor = self.getMD2ImageXRatio() + return float(microns * pixels_per_micron) / img_scale_factor + + def screenYmicrons2pixels(self, microns): + pixels_per_mm = 1 / self.camera.scale_y.get() + pixels_per_micron = pixels_per_mm / 1000.0 + img_scale_factor = self.getMD2ImageYRatio() + return float(microns * pixels_per_micron) / img_scale_factor + + def getMD2ImageXRatio(self): + md2_img_width = daq_utils.highMagPixX + lsdc_img_width = daq_utils.screenPixX + return float(md2_img_width) / float(lsdc_img_width) + + def getMD2ImageYRatio(self): + md2_img_height = daq_utils.highMagPixY + lsdc_img_height = daq_utils.screenPixY + return float(md2_img_height) / float(lsdc_img_height) + + def getMD2BeamCenterX(self): + return self.md2.center_pixel_x.get() / self.getMD2ImageXRatio() + + def getMD2BeamCenterY(self): + return self.md2.center_pixel_y.get() / self.getMD2ImageYRatio() + + def definePolyRaster( + self, raster_w, raster_h, stepsizeXPix, stepsizeYPix, point_x, point_y + ): # all come in as pixels, raster_w and raster_h are bounding box of drawn graphic + # raster status - 0=nothing done, 1=run, 2=displayed + stepTime = float(self.exp_time_ledit.text()) + stepsize = float(self.rasterStepEdit.text()) + if (stepsize / 1000.0) / stepTime > 2.0: + self.popupServerMessage( + "Stage speed exceeded. Increase exposure time, or decrease step size. Limit is 2mm/s." + ) + self.eraseCB() + return + + try: + beamWidth = float(self.beamWidth_ledit.text()) + beamHeight = float(self.beamHeight_ledit.text()) + except ValueError: + logger.error("bad value for beam width or beam height") + self.popupServerMessage("bad value for beam width or beam height") + return + if self.scannerType == "PI": + rasterDef = { + "rasterType": "normal", + "beamWidth": beamWidth, + "beamHeight": beamHeight, + "status": RasterStatus.NEW.value, + "x": self.gon.x.val() + self.sampFineX_pv.get(), + "y": self.gon.y.val() + self.sampFineY_pv.get(), + "z": self.gon.z.val() + self.sampFineZ_pv.get(), + "omega": self.gon.omega.val(), + "stepsize": stepsize, + "rowDefs": [], + } # just storing step as microns, not using her + else: + rasterDef = { + "rasterType": "normal", + "beamWidth": beamWidth, + "beamHeight": beamHeight, + "status": RasterStatus.NEW.value, + "x": self.gon.x.val(), + "y": self.gon.y.val(), + "z": self.gon.z.val(), + "omega": self.gon.omega.val(), + "stepsize": stepsize, + "rowDefs": [], + } # just storing step as microns, not using here + numsteps_h = int( + raster_w / stepsizeXPix + ) # raster_w = width,goes to numsteps horizonatl + numsteps_v = int(raster_h / stepsizeYPix) + if numsteps_h == 2: + numsteps_h = 1 # fix slop in user single line attempt + if numsteps_h % 2 == 0: # make odd numbers of rows and columns + numsteps_h = numsteps_h + 1 + if numsteps_v % 2 == 0: + numsteps_v = numsteps_v + 1 + rasterDef["numCells"] = numsteps_h * numsteps_v + point_offset_x = -(numsteps_h * stepsizeXPix) / 2 + point_offset_y = -(numsteps_v * stepsizeYPix) / 2 + if (numsteps_h == 1) or ( + numsteps_v > numsteps_h and getBlConfig("vertRasterOn") + ): # vertical raster + for i in range(numsteps_h): + rowCellCount = 0 + for j in range(numsteps_v): + newCellX = point_x + (i * stepsizeXPix) + point_offset_x + newCellY = point_y + (j * stepsizeYPix) + point_offset_y + if rowCellCount == 0: # start of a new row + rowStartX = newCellX + rowStartY = newCellY + rowCellCount = rowCellCount + 1 + if ( + rowCellCount != 0 + ): # test for no points in this row of the bounding rect are in the poly? + vectorStartX = self.screenXPixels2microns( + rowStartX - self.centerMarker.x() - self.centerMarkerCharOffsetX + ) + vectorEndX = vectorStartX + vectorStartY = self.screenYPixels2microns( + rowStartY - self.centerMarker.y() - self.centerMarkerCharOffsetY + ) + vectorEndY = vectorStartY + self.screenYPixels2microns( + rowCellCount * stepsizeYPix + ) + newRowDef = { + "start": {"x": vectorStartX, "y": vectorStartY}, + "end": {"x": vectorEndX, "y": vectorEndY}, + "numsteps": rowCellCount, + } + rasterDef["rowDefs"].append(newRowDef) + else: # horizontal raster + for i in range(numsteps_v): + rowCellCount = 0 + for j in range(numsteps_h): + newCellX = point_x + (j * stepsizeXPix) + point_offset_x + newCellY = point_y + (i * stepsizeYPix) + point_offset_y + if rowCellCount == 0: # start of a new row + rowStartX = newCellX + rowStartY = newCellY + rowCellCount = rowCellCount + 1 + if ( + rowCellCount != 0 + ): # testing for no points in this row of the bounding rect are in the poly? + vectorStartX = self.screenXPixels2microns( + rowStartX - self.centerMarker.x() - self.centerMarkerCharOffsetX + ) + vectorEndX = vectorStartX + self.screenXPixels2microns( + rowCellCount * stepsizeXPix + ) # this looks better + vectorStartY = self.screenYPixels2microns( + rowStartY - self.centerMarker.y() - self.centerMarkerCharOffsetY + ) + vectorEndY = vectorStartY + newRowDef = { + "start": {"x": vectorStartX, "y": vectorStartY}, + "end": {"x": vectorEndX, "y": vectorEndY}, + "numsteps": rowCellCount, + } + rasterDef["rowDefs"].append(newRowDef) + setBlConfig("rasterDefaultWidth", float(self.osc_range_ledit.text())) + setBlConfig("rasterDefaultTime", float(self.exp_time_ledit.text())) + setBlConfig("rasterDefaultTrans", float(self.transmission_ledit.text())) + + self.addSampleRequestCB(rasterDef) + return # short circuit + + def rasterIsDrawn(self, rasterReq): + for i in range(len(self.rasterList)): + if self.rasterList[i] != None: + if self.rasterList[i]["uid"] == rasterReq["uid"]: + return True + return False + + def drawPolyRaster( + self, rasterReq, x=-1, y=-1, z=-1 + ): # rasterDef in microns,offset from center, need to convert to pixels to draw, mainly this is for displaying autoRasters, but also called in zoom change + try: + rasterDef = rasterReq["request_obj"]["rasterDef"] + except KeyError: + return + beamSize = self.screenXmicrons2pixels(rasterDef["beamWidth"]) + stepsizeX = self.screenXmicrons2pixels(rasterDef["stepsize"]) + stepsizeY = self.screenYmicrons2pixels(rasterDef["stepsize"]) + pen = QtGui.QPen(QtCore.Qt.red) + newRasterCellList = [] + try: + if ( + rasterDef["rowDefs"][0]["start"]["y"] + == rasterDef["rowDefs"][0]["end"]["y"] + ): # this is a horizontal raster + rasterDir = "horizontal" + else: + rasterDir = "vertical" + except IndexError: + return + for i in range(len(rasterDef["rowDefs"])): + rowCellCount = 0 + for j in range(rasterDef["rowDefs"][i]["numsteps"]): + if rasterDir == "horizontal": + newCellX = ( + self.screenXmicrons2pixels( + rasterDef["rowDefs"][i]["start"]["x"] + ) + + (j * stepsizeX) + + self.centerMarker.x() + + self.centerMarkerCharOffsetX + ) + newCellY = ( + self.screenYmicrons2pixels( + rasterDef["rowDefs"][i]["start"]["y"] + ) + + self.centerMarker.y() + + self.centerMarkerCharOffsetY + ) + else: + newCellX = ( + self.screenXmicrons2pixels( + rasterDef["rowDefs"][i]["start"]["x"] + ) + + self.centerMarker.x() + + self.centerMarkerCharOffsetX + ) + newCellY = ( + self.screenYmicrons2pixels( + rasterDef["rowDefs"][i]["start"]["y"] + ) + + (j * stepsizeY) + + self.centerMarker.y() + + self.centerMarkerCharOffsetY + ) + if rowCellCount == 0: # start of a new row + rowStartX = newCellX + rowStartY = newCellY + newCellX = int(newCellX) + newCellY = int(newCellY) + newCell = RasterCell(newCellX, newCellY, stepsizeX, stepsizeY, self) + newRasterCellList.append(newCell) + newCell.setPen(pen) + rowCellCount = rowCellCount + 1 # really just for test of new row + newItemGroup = RasterGroup(self) + self.scene.addItem(newItemGroup) + for i in range(len(newRasterCellList)): + newItemGroup.addToGroup(newRasterCellList[i]) + newRasterGraphicsDesc = { + "uid": rasterReq["uid"], + "coords": { + "x": rasterDef["x"], + "y": rasterDef["y"], + "z": rasterDef["z"], + "omega": rasterDef["omega"], + }, + "graphicsItem": newItemGroup, + } + self.rasterList.append(newRasterGraphicsDesc) + + def sampleFrameCB(self): + frames = str(self.capture.get(cv2.CAP_PROP_BUFFERSIZE)) + text = frames + " frames" + self.imageScaleText = self.scene.addSimpleText( + text, font=QtGui.QFont("Times", 13) + ) + ''' + for thread in self.active_camera_threads: + if not thread.is_alive(): # remove old threads + self.active_camera_threads.remove(thread) + if not len(self.active_camera_threads) > 0: + self.active_camera_threads.append(threading.Thread(target=self.timerSampleRefresh)) + self.active_camera_threads[-1].start() + + if not self.frame_queue.empty(): + self.pixmap_item.setPixmap(self.frame_queue.get()) + ''' + def timerSampleRefresh(self): + if self.capture is None: + return + # uncomment this for frame resizing + # self.currentFrame = self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640) + # self.currentFrame = self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 512) + start_time = time.time() + retval, self.currentFrame = self.capture.read() + self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 1) + capture_time = time.time() + if self.currentFrame is None: + logger.debug( + "no frame read from stream URL - ensure the URL does not end with newline and that the filename is correct" + ) + return # maybe stop the timer also??? + height, width = self.currentFrame.shape[:2] + qimage = QtGui.QImage( + self.currentFrame, width, height, 3 * width, QtGui.QImage.Format_RGB888 + ) + qimage = qimage.rgbSwapped() + pixmap_orig = QtGui.QPixmap.fromImage(qimage) + self.pixmap_item.setPixmap(pixmap_orig) + #while(self.frame_queue.qsize() > 1): + # self.frame_queue.get() # remove old frames + #self.frame_queue.put(pixmap_orig) + end_time = time.time() + #logger.info(f"capture time: {capture_time - start_time}, total time: {end_time - start_time}") + + def sceneKey(self, event): + if ( + event.key() == QtCore.Qt.Key_Delete + or event.key() == QtCore.Qt.Key_Backspace + ): + for i in range(len(self.rasterList)): + if self.rasterList[i] != None: + if self.rasterList[i]["graphicsItem"].isSelected(): + try: + sceneReq = db_lib.getRequestByID(self.rasterList[i]["uid"]) + if sceneReq != None: + self.selectedSampleID = sceneReq["sample"] + db_lib.deleteRequest(sceneReq)["uid"] + except AttributeError: + pass + self.scene.removeItem(self.rasterList[i]["graphicsItem"]) + self.rasterList[i] = None + self.treeChanged_pv.put(1) + for i in range(len(self.centeringMarksList)): + if self.centeringMarksList[i] != None: + if self.centeringMarksList[i]["graphicsItem"].isSelected(): + self.scene.removeItem( + self.centeringMarksList[i]["graphicsItem"] + ) + self.centeringMarksList[i] = None + + def pixelSelect(self, event): + super(QtWidgets.QGraphicsPixmapItem, self.pixmap_item).mousePressEvent(event) + x_click = float(event.pos().x()) + y_click = float(event.pos().y()) + penGreen = QtGui.QPen(QtCore.Qt.green) + penRed = QtGui.QPen(QtCore.Qt.red) + if self.vidActionDefineCenterRadio.isChecked(): + self.vidActionC2CRadio.setChecked( + True + ) # because it's easy to forget defineCenter is on + if self.zoom4Radio.isChecked(): + comm_s = ( + "changeImageCenterHighMag(" + + str(x_click) + + "," + + str(y_click) + + ",1)" + ) + elif self.zoom3Radio.isChecked(): + comm_s = ( + "changeImageCenterHighMag(" + + str(x_click) + + "," + + str(y_click) + + ",0)" + ) + if self.zoom2Radio.isChecked(): + comm_s = ( + "changeImageCenterLowMag(" + + str(x_click) + + "," + + str(y_click) + + ",1)" + ) + elif self.zoom1Radio.isChecked(): + comm_s = ( + "changeImageCenterLowMag(" + + str(x_click) + + "," + + str(y_click) + + ",0)" + ) + self.send_to_server(comm_s) + return + if self.vidActionRasterDefRadio.isChecked(): + self.click_positions.append(event.pos()) + self.polyPointItems.append( + self.scene.addEllipse(x_click, y_click, 4, 4, penRed) + ) + if len(self.click_positions) == 4: + self.drawInteractiveRasterCB() + return + fov = self.getCurrentFOV() + correctedC2C_x = self.getMD2BeamCenterX() + ( + x_click - (self.centerMarker.x() + self.centerMarkerCharOffsetX) + ) + correctedC2C_y = self.getMD2BeamCenterY() + ( + y_click - (self.centerMarker.y() + self.centerMarkerCharOffsetY) + ) + + current_viewangle = daq_utils.mag1ViewAngle + if self.zoom2Radio.isChecked(): + current_viewangle = daq_utils.mag2ViewAngle + elif self.zoom3Radio.isChecked(): + current_viewangle = daq_utils.mag3ViewAngle + elif self.zoom4Radio.isChecked(): + current_viewangle = daq_utils.mag4ViewAngle + + if self.threeClickCount > 0: # 3-click centering + self.threeClickCount = self.threeClickCount + 1 + if daq_utils.exporter_enabled: + correctedC2C_x = x_click + ((daq_utils.screenPixX/2) - (self.centerMarker.x() + self.centerMarkerCharOffsetX)) + correctedC2C_y = y_click + ((daq_utils.screenPixY/2) - (self.centerMarker.y() + self.centerMarkerCharOffsetY)) + lsdc_x = daq_utils.screenPixX + lsdc_y = daq_utils.screenPixY + md2_x = self.md2.center_pixel_x.get() * 2 + md2_y = self.md2.center_pixel_y.get() * 2 + scale_x = md2_x / lsdc_x + scale_y = md2_y / lsdc_y + correctedC2C_x = correctedC2C_x * scale_x + correctedC2C_y = correctedC2C_y * scale_y + self.md2.centring_click.put(f"{correctedC2C_x} {correctedC2C_y}") + if self.threeClickCount == 4: + self.threeClickCount = 0 + self.click3Button.setStyleSheet("background-color: None") + return + else: + comm_s = f'center_on_click({correctedC2C_x},{correctedC2C_y},{fov["x"]},{fov["y"]},source="screen",jog=90,viewangle={current_viewangle})' + else: + comm_s = f'center_on_click({correctedC2C_x},{correctedC2C_y},{fov["x"]},{fov["y"]},source="screen",maglevel=0,viewangle={current_viewangle})' + if not self.vidActionRasterExploreRadio.isChecked(): + self.aux_send_to_server(comm_s) + if self.threeClickCount == 4: + self.threeClickCount = 0 + self.click3Button.setStyleSheet("background-color: None") + return + + def editScreenParamsCB(self): + self.screenDefaultsDialog = ScreenDefaultsDialog(self) + self.screenDefaultsDialog.show() + + def editSelectedRequestsCB(self): + selmod = self.dewarTree.selectionModel() + selection = selmod.selection() + indexes = selection.indexes() + singleRequest = 1 + for i in range(len(indexes)): + item = self.dewarTree.model.itemFromIndex(indexes[i]) + itemData = str(item.data(32)) + itemDataType = str(item.data(33)) + if itemDataType == "request": + self.selectedSampleRequest = db_lib.getRequestByID(itemData) + self.editSampleRequestCB(singleRequest) + singleRequest = 0 + self.treeChanged_pv.put(1) + + def editSampleRequestCB(self, singleRequest): + colRequest = self.selectedSampleRequest + reqObj = colRequest["request_obj"] + if not self.validateAllFields(): + return + try: + reqObj["sweep_start"] = float(self.osc_start_ledit.text()) + reqObj["sweep_end"] = float(self.osc_end_ledit.text()) + float( + self.osc_start_ledit.text() + ) + reqObj["img_width"] = float(self.osc_range_ledit.text()) + reqObj["exposure_time"] = float(self.exp_time_ledit.text()) + reqObj["detDist"] = float(self.detDistMotorEntry.getEntry().text()) + reqObj["resolution"] = float(self.resolution_ledit.text()) + if ( + singleRequest == 1 + ): # a touch kludgy, but I want to be able to edit parameters for multiple requests w/o screwing the data loc info + reqObj["file_prefix"] = str(self.dataPathGB.prefix_ledit.text()) + reqObj["basePath"] = str(self.dataPathGB.base_path_ledit.text()) + reqObj["directory"] = str(self.dataPathGB.dataPath_ledit.text()) + reqObj["file_number_start"] = int( + self.dataPathGB.file_numstart_ledit.text() + ) + reqObj["attenuation"] = float(self.transmission_ledit.text()) + reqObj["slit_width"] = float(self.beamWidth_ledit.text()) + reqObj["slit_height"] = float(self.beamHeight_ledit.text()) + reqObj["energy"] = float(self.energy_ledit.text()) + wave = daq_utils.energy2wave(float(self.energy_ledit.text()), digits=6) + reqObj["wavelength"] = wave + except ValueError: + message = "Please ensure that all boxes that expect numerical values have numbers in them" + logger.error(message) + self.popupServerMessage(f"{message}") + return + reqObj["fastDP"] = ( + self.staffScreenDialog.fastDPCheckBox.isChecked() + or self.fastEPCheckBox.isChecked() + or self.dimpleCheckBox.isChecked() + ) + reqObj["fastEP"] = self.fastEPCheckBox.isChecked() + reqObj["dimple"] = self.dimpleCheckBox.isChecked() + reqObj["xia2"] = self.xia2CheckBox.isChecked() + reqObj["protocol"] = str(self.protoComboBox.currentText()) + if reqObj["protocol"] == "vector" or reqObj["protocol"] == "stepVector": + reqObj["vectorParams"]["fpp"] = int(self.vectorFPP_ledit.text()) + colRequest["request_obj"] = reqObj + db_lib.updateRequest(colRequest) + self.treeChanged_pv.put(1) + + def addRequestsToAllSelectedCB(self): + if ( + self.protoComboBox.currentText() == "raster" + or self.protoComboBox.currentText() == "stepRaster" + ): # it confused people when they didn't need to add rasters explicitly + return + selmod = self.dewarTree.selectionModel() + selection = selmod.selection() + indexes = selection.indexes() + try: + progressInc = 100.0 / float(len(indexes)) + except ZeroDivisionError: + self.popupServerMessage("Select a sample to perform the request on!") + return + self.progressDialog.setWindowTitle("Creating Requests") + self.progressDialog.show() + if ( + getBlConfig("queueCollect") == 1 + ): # If queue collect is ON only consider selected samples/requests and add a request to each + samplesConsidered = set() + for i in range(len(indexes)): + self.progressDialog.setValue(int((i + 1) * progressInc)) + item = self.dewarTree.model.itemFromIndex(indexes[i]) + itemData = str(item.data(32)) + itemDataType = str(item.data(33)) + if itemDataType == "sample": + self.selectedSampleID = itemData + elif itemDataType == "request": + selectedSampleRequest = db_lib.getRequestByID(item.data(32)) + self.selectedSampleID = selectedSampleRequest["sample"] + + if self.selectedSampleID in samplesConsidered: # If a request is already added to the sample, move on + continue + + try: + self.selectedSampleRequest = daq_utils.createDefaultRequest( + self.selectedSampleID + ) + except KeyError: + self.popupServerMessage("Please select a sample!") + self.progressDialog.close() + return + if len(indexes) > 1: + self.dataPathGB.setFilePrefix_ledit( + str(self.selectedSampleRequest["request_obj"]["file_prefix"]) + ) + self.dataPathGB.setDataPath_ledit( + str(self.selectedSampleRequest["request_obj"]["directory"]) + ) + self.EScanDataPathGB.setFilePrefix_ledit( + str(self.selectedSampleRequest["request_obj"]["file_prefix"]) + ) + self.EScanDataPathGB.setDataPath_ledit( + str(self.selectedSampleRequest["request_obj"]["directory"]) + ) + if itemDataType != "container": + self.addSampleRequestCB(selectedSampleID=self.selectedSampleID) + samplesConsidered.add(self.selectedSampleID) + else: # If queue collect is off does not matter how many requests you select only one will be added to current pin + self.selectedSampleID = self.mountedPin_pv.get() + self.selectedSampleRequest = daq_utils.createDefaultRequest( + self.selectedSampleID + ) + self.dataPathGB.setFilePrefix_ledit( + str(self.selectedSampleRequest["request_obj"]["file_prefix"]) + ) + self.dataPathGB.setDataPath_ledit( + str(self.selectedSampleRequest["request_obj"]["directory"]) + ) + self.EScanDataPathGB.setFilePrefix_ledit( + str(self.selectedSampleRequest["request_obj"]["file_prefix"]) + ) + self.EScanDataPathGB.setDataPath_ledit( + str(self.selectedSampleRequest["request_obj"]["directory"]) + ) + self.addSampleRequestCB(selectedSampleID=self.selectedSampleID) + + self.progressDialog.close() + self.treeChanged_pv.put(1) + + def addSampleRequestCB(self, rasterDef=None, selectedSampleID=None): + if self.selectedSampleID != None: + try: + sample = db_lib.getSampleByID(self.selectedSampleID) + propNum = sample["proposalID"] + except KeyError: + propNum = 999999 + if propNum == None: + propNum = 999999 + if propNum != daq_utils.getProposalID(): + logger.info("setting proposal in add request") + daq_utils.setProposalID(propNum, createVisit=True) + + if getBlConfig("queueCollect") == 0: + if self.mountedPin_pv.get() != self.selectedSampleID: + self.selectedSampleID = self.mountedPin_pv.get() + + if not self.validateAllFields(): + return + # skinner, not pretty below the way stuff is duplicated. + try: + if ( + float(self.osc_end_ledit.text()) < float(self.osc_range_ledit.text()) + ) and str(self.protoComboBox.currentText()) != "eScan": + self.popupServerMessage("Osc range less than Osc width") + return + except ValueError: + message = ( + "Please ensure oscillation end and oscillation range are valid numbers" + ) + logger.error(message) + self.popupServerMessage(f"{message}") + return + + if self.periodicTable.isVisible(): + if self.periodicTable.eltCurrent != None: + symbol = self.periodicTable.eltCurrent.symbol + targetEdge = element_info[symbol][2] + if daq_utils.beamline == "fmx": + mcaRoiLo = element_info[symbol][4] + mcaRoiHi = element_info[symbol][5] + else: + mcaRoiLo = self.XRFInfoDict[symbol] - 25 + mcaRoiHi = self.XRFInfoDict[symbol] + 25 + targetEnergy = Elements.Element[symbol]["binding"][targetEdge] + currentEnergy = float(self.energy_ledit.text()) + energyDiff = abs( + currentEnergy - targetEnergy * 1000 + ) # targetEnergy is in keV, LSDC energy is in eV + if energyDiff >= 20: + message = f"Please choose an element with an edge closer to {currentEnergy}. The energy difference to {symbol} is {energyDiff:.1f}" + logger.error(message) + self.popupServerMessage(f"{message}") + return + colRequest = daq_utils.createDefaultRequest(self.selectedSampleID) + sampleName = str(db_lib.getSampleNamebyID(colRequest["sample"])) + runNum = db_lib.incrementSampleRequestCount(colRequest["sample"]) + ( + puckPosition, + samplePositionInContainer, + containerID, + ) = db_lib.getCoordsfromSampleID( + daq_utils.beamline, colRequest["sample"] + ) + reqObj = get_request_object_escan( + colRequest["request_obj"], + self.periodicTable.eltCurrent.symbol, + runNum, + self.EScanDataPathGB.prefix_ledit.text(), + self.EScanDataPathGB.base_path_ledit.text(), + sampleName, + containerID, + samplePositionInContainer, + self.EScanDataPathGB.file_numstart_ledit.text(), + self.exp_time_ledit.text(), + targetEnergy, + self.escan_steps_ledit.text(), + self.escan_stepsize_ledit.text(), + ) + reqObj["detDist"] = float(self.detDistMotorEntry.getEntry().text()) + reqObj["attenuation"] = float(self.transmission_ledit.text()) + reqObj["mcaRoiLo"] = mcaRoiLo + reqObj["mcaRoiHi"] = mcaRoiHi + + colRequest["request_obj"] = reqObj + newSampleRequestID = db_lib.addRequesttoSample( + self.selectedSampleID, + reqObj["protocol"], + daq_utils.owner, + reqObj, + priority=5000, + proposalID=daq_utils.getProposalID(), + ) + # attempt here to select a newly created request. + self.SelectedItemData = newSampleRequestID + + if ( + selectedSampleID == None + ): # this is a temp kludge to see if this is called from addAll + self.treeChanged_pv.put(1) + else: + logger.info("choose an element and try again") + return + + # I don't like the code duplication, but one case is the mounted sample and selected centerings - so it's in a loop for multiple reqs, the other requires autocenter. + if (self.mountedPin_pv.get() == self.selectedSampleID) and ( + len(self.centeringMarksList) != 0 + ): + selectedCenteringFound = 0 + for i in range(len(self.centeringMarksList)): + if self.centeringMarksList[i]["graphicsItem"].isSelected(): + selectedCenteringFound = 1 + colRequest = daq_utils.createDefaultRequest(self.selectedSampleID) + sampleName = str(db_lib.getSampleNamebyID(colRequest["sample"])) + runNum = db_lib.incrementSampleRequestCount(colRequest["sample"]) + ( + puckPosition, + samplePositionInContainer, + containerID, + ) = db_lib.getCoordsfromSampleID( + daq_utils.beamline, colRequest["sample"] + ) + reqObj = colRequest["request_obj"] + try: + reqObj["runNum"] = runNum + reqObj["sweep_start"] = float(self.osc_start_ledit.text()) + reqObj["sweep_end"] = float(self.osc_end_ledit.text()) + float( + self.osc_start_ledit.text() + ) + reqObj["img_width"] = float(self.osc_range_ledit.text()) + setBlConfig( + "screen_default_width", float(self.osc_range_ledit.text()) + ) + setBlConfig( + "screen_default_time", float(self.exp_time_ledit.text()) + ) + setBlConfig("stdTrans", float(self.transmission_ledit.text())) + setBlConfig( + "screen_default_dist", + float(self.detDistMotorEntry.getEntry().text()), + ) + reqObj["exposure_time"] = float(self.exp_time_ledit.text()) + reqObj["resolution"] = float(self.resolution_ledit.text()) + reqObj["file_prefix"] = str( + self.dataPathGB.prefix_ledit.text() + "_C" + str(i + 1) + ) + reqObj["basePath"] = str(self.dataPathGB.base_path_ledit.text()) + reqObj["directory"] = ( + str(self.dataPathGB.base_path_ledit.text()) + + "/" + + str(daq_utils.getVisitName()) + + "/" + + sampleName + + "/" + + str(runNum) + + "/" + + db_lib.getContainerNameByID(containerID) + + "_" + + str(samplePositionInContainer + 1) + + "/" + ) + reqObj["file_number_start"] = int( + self.dataPathGB.file_numstart_ledit.text() + ) + reqObj["attenuation"] = float(self.transmission_ledit.text()) + reqObj["slit_width"] = float(self.beamWidth_ledit.text()) + reqObj["slit_height"] = float(self.beamHeight_ledit.text()) + reqObj["energy"] = float(self.energy_ledit.text()) + wave = daq_utils.energy2wave( + float(self.energy_ledit.text()), digits=6 + ) + reqObj["wavelength"] = wave + except ValueError: + message = "Please ensure that all boxes that expect numerical values have numbers in them" + logger.error(message) + self.popupServerMessage(f"{message}") + return + reqObj["detDist"] = float(self.detDistMotorEntry.getEntry().text()) + reqObj["protocol"] = str(self.protoComboBox.currentText()) + reqObj["pos_x"] = float( + self.centeringMarksList[i]["sampCoords"]["x"] + ) + reqObj["pos_y"] = float( + self.centeringMarksList[i]["sampCoords"]["y"] + ) + reqObj["pos_z"] = float( + self.centeringMarksList[i]["sampCoords"]["z"] + ) + reqObj["fastDP"] = ( + self.staffScreenDialog.fastDPCheckBox.isChecked() + or self.fastEPCheckBox.isChecked() + or self.dimpleCheckBox.isChecked() + ) + reqObj["fastEP"] = self.fastEPCheckBox.isChecked() + reqObj["dimple"] = self.dimpleCheckBox.isChecked() + reqObj["xia2"] = self.xia2CheckBox.isChecked() + if ( + reqObj["protocol"] == "characterize" + or reqObj["protocol"] == "ednaCol" + ): + characterizationParams = { + "aimed_completeness": float( + self.characterizeCompletenessEdit.text() + ), + "aimed_multiplicity": str( + self.characterizeMultiplicityEdit.text() + ), + "aimed_resolution": float(self.characterizeResoEdit.text()), + "aimed_ISig": float(self.characterizeISIGEdit.text()), + } + reqObj["characterizationParams"] = characterizationParams + colRequest["request_obj"] = reqObj + newSampleRequestID = db_lib.addRequesttoSample( + self.selectedSampleID, + reqObj["protocol"], + daq_utils.owner, + reqObj, + priority=5000, + proposalID=daq_utils.getProposalID(), + ) + # attempt here to select a newly created request. + self.SelectedItemData = newSampleRequestID + if selectedCenteringFound == 0: + message = QtWidgets.QErrorMessage(self) + message.setModal(False) + message.showMessage("You need to select a centering.") + else: # autocenter or interactive + colRequest = self.selectedSampleRequest + try: + sampleName = str(db_lib.getSampleNamebyID(colRequest["sample"])) + except KeyError: + logger.error("no sample selected") + self.popupServerMessage("no sample selected") + return + ( + puckPosition, + samplePositionInContainer, + containerID, + ) = db_lib.getCoordsfromSampleID(daq_utils.beamline, colRequest["sample"]) + runNum = db_lib.incrementSampleRequestCount(colRequest["sample"]) + reqObj = colRequest["request_obj"] + centeringOption = str(self.centeringComboBox.currentText()) + reqObj["centeringOption"] = centeringOption + if ( + centeringOption == "Interactive" + and self.mountedPin_pv.get() == self.selectedSampleID + ) or centeringOption == "Testing": # user centered manually + reqObj["pos_x"] = float(self.gon.x.val()) + reqObj["pos_y"] = float(self.gon.y.val()) + reqObj["pos_z"] = float(self.gon.z.val()) + reqObj["runNum"] = runNum + try: + reqObj["sweep_start"] = float(self.osc_start_ledit.text()) + reqObj["sweep_end"] = float(self.osc_end_ledit.text()) + float( + self.osc_start_ledit.text() + ) + reqObj["img_width"] = float(self.osc_range_ledit.text()) + reqObj["exposure_time"] = float(self.exp_time_ledit.text()) + if rasterDef == None and reqObj["protocol"] != "burn": + setBlConfig( + "screen_default_width", float(self.osc_range_ledit.text()) + ) + setBlConfig( + "screen_default_time", float(self.exp_time_ledit.text()) + ) + setBlConfig("stdTrans", float(self.transmission_ledit.text())) + setBlConfig( + "screen_default_dist", + float(self.detDistMotorEntry.getEntry().text()), + ) + reqObj["resolution"] = float(self.resolution_ledit.text()) + reqObj["directory"] = ( + str(self.dataPathGB.base_path_ledit.text()) + + "/" + + str(daq_utils.getVisitName()) + + "/" + + str(self.dataPathGB.prefix_ledit.text()) + + "/" + + str(runNum) + + "/" + + db_lib.getContainerNameByID(containerID) + + "_" + + str(samplePositionInContainer + 1) + + "/" + ) + reqObj["basePath"] = str(self.dataPathGB.base_path_ledit.text()) + reqObj["file_prefix"] = str(self.dataPathGB.prefix_ledit.text()) + reqObj["file_number_start"] = int( + self.dataPathGB.file_numstart_ledit.text() + ) + if abs(reqObj["sweep_end"] - reqObj["sweep_start"]) < 5.0: + reqObj["fastDP"] = False + reqObj["fastEP"] = False + reqObj["dimple"] = False + else: + reqObj["fastDP"] = ( + self.staffScreenDialog.fastDPCheckBox.isChecked() + or self.fastEPCheckBox.isChecked() + or self.dimpleCheckBox.isChecked() + ) + reqObj["fastEP"] = self.fastEPCheckBox.isChecked() + reqObj["dimple"] = self.dimpleCheckBox.isChecked() + reqObj["xia2"] = self.xia2CheckBox.isChecked() + reqObj["attenuation"] = float(self.transmission_ledit.text()) + reqObj["slit_width"] = float(self.beamWidth_ledit.text()) + reqObj["slit_height"] = float(self.beamHeight_ledit.text()) + reqObj["energy"] = float(self.energy_ledit.text()) + except ValueError: + message = "Please ensure that all boxes that expect numerical values have numbers in them" + logger.error(message) + self.popupServerMessage(f"{message}") + return + try: + wave = daq_utils.energy2wave(float(self.energy_ledit.text()), digits=6) + except ValueError: + wave = 1.1 + + reqObj["wavelength"] = wave + reqObj["protocol"] = str(self.protoComboBox.currentText()) + try: + reqObj["detDist"] = float(self.detDistMotorEntry.getEntry().text()) + except ValueError: + new_distance = 502.0 + logger.error("set dist to %s in exception handler 1" % new_distance) + reqObj["detDist"] = new_distance + if reqObj["protocol"] == "multiCol" or reqObj["protocol"] == "multiColQ": + reqObj["gridStep"] = float(self.rasterStepEdit.text()) + reqObj["diffCutoff"] = float(self.multiColCutoffEdit.text()) + if reqObj["protocol"] == "rasterScreen": + reqObj["gridStep"] = float(self.rasterStepEdit.text()) + if rasterDef != None: + reqObj["rasterDef"] = rasterDef + reqObj["gridStep"] = float(self.rasterStepEdit.text()) + if reqObj["protocol"] == "characterize" or reqObj["protocol"] == "ednaCol": + characterizationParams = { + "aimed_completeness": float( + self.characterizeCompletenessEdit.text() + ), + "aimed_multiplicity": str(self.characterizeMultiplicityEdit.text()), + "aimed_resolution": float(self.characterizeResoEdit.text()), + "aimed_ISig": float(self.characterizeISIGEdit.text()), + } + reqObj["characterizationParams"] = characterizationParams + if reqObj["protocol"] == "vector" or reqObj["protocol"] == "stepVector": + if float(self.osc_end_ledit.text()) < 5.0: + self.popupServerMessage( + "Vector oscillation must be at least 5.0 degrees." + ) + return + selectedCenteringFound = 1 + try: + x_vec, y_vec, z_vec, trans_total = self.updateVectorLengthAndSpeed() + framesPerPoint = int(self.vectorFPP_ledit.text()) + vectorParams = { + "vecStart": self.vectorStart["coords"], + "vecEnd": self.vectorEnd["coords"], + "x_vec": x_vec, + "y_vec": y_vec, + "z_vec": z_vec, + "trans_total": trans_total, + "fpp": framesPerPoint, + } + reqObj["vectorParams"] = vectorParams + except Exception as e: + if self.vectorStart == None: + self.popupServerMessage("Vector start must be defined.") + return + elif self.vectorEnd == None: + self.popupServerMessage("Vector end must be defined.") + return + logger.error("Exception while getting vector parameters: %s" % e) + pass + colRequest["request_obj"] = reqObj + newSampleRequestID = db_lib.addRequesttoSample( + self.selectedSampleID, + reqObj["protocol"], + daq_utils.owner, + reqObj, + priority=5000, + proposalID=daq_utils.getProposalID(), + ) + # attempt here to select a newly created request. + self.SelectedItemData = newSampleRequestID + newSampleRequest = db_lib.getRequestByID(newSampleRequestID) + if rasterDef != None: + self.rasterDefList.append(newSampleRequest) + self.drawPolyRaster(newSampleRequest) + if ( + selectedSampleID == None + ): # this is a temp kludge to see if this is called from addAll + self.treeChanged_pv.put(1) + + def cloneRequestCB(self): + self.eraseCB() + colRequest = self.selectedSampleRequest + reqObj = colRequest["request_obj"] + if "rasterDef" in reqObj: + self.addSampleRequestCB(reqObj["rasterDef"]) + + def collectQueueCB(self): + currentRequest = db_lib.popNextRequest(daq_utils.beamline) + if currentRequest == {}: + self.addRequestsToAllSelectedCB() + logger.info("running queue") + self.send_to_server("runDCQueue()") + + def warmupGripperCB(self): + self.send_to_server("warmupGripper()") + + def dryGripperCB(self): + self.send_to_server("dryGripper()") + + def enableTScreenGripperCB(self): + self.send_to_server("enableDewarTscreen()") + + def parkGripperCB(self): + self.send_to_server("parkGripper()") + + def restartServerCB(self): + if self.controlEnabled(): + msg = "Desperation move. Are you sure?" + #self.timerSample.stop() + reply = QtWidgets.QMessageBox.question( + self, + "Message", + msg, + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No, + ) + #self.timerSample.start(SAMPLE_TIMER_DELAY) + if reply == QtWidgets.QMessageBox.Yes: + if daq_utils.beamline == "fmx" or daq_utils.beamline == "amx": + restart_pv = PV(daq_utils.beamlineComm + "RestartServerSignal") + restart_pv.put(not (restart_pv.get())) + else: + logger.error("Not restarting server - unknown beamline") + else: + self.popupServerMessage("You don't have control") + + def openPhotonShutterCB(self): + self.photonShutterOpen_pv.put(1) + + def popUserScreenCB(self): + if self.controlEnabled(): + self.userScreenDialog.show() + else: + self.popupServerMessage("You don't have control") + + def parkRobotCB(self): + self.send_to_server("parkRobot()") + + def closePhotonShutterCB(self): + self.photonShutterClose_pv.put(1) + + def removePuckCB(self): + #self.timerSample.stop() + dewarPos, ok = DewarDialog.getDewarPos(parent=self, action="remove") + #self.timerSample.start(SAMPLE_TIMER_DELAY) + + def transform_vector_coords(self, prev_coords, current_raw_coords): + """Updates y and z co-ordinates of vector points when they are moved + + This function tweaks the y and z co-ordinates such that when a vector start or + end point is adjusted in the 2-D plane of the screen, it maintains the points' location + in the 3rd dimension perpendicular to the screen + + Args: + prev_coords: Dictionary with x,y and z co-ordinates of the previous location of the sample + current_raw_coords: Dictionary with x, y and z co-ordinates of the sample derived from the goniometer + PVs + omega: Omega of the Goniometer (usually RBV) + + Returns: + A dictionary mapping x, y and z to tweaked coordinates + """ + + # Transform z from prev point and y from current point to lab coordinate system + _, _, zLabPrev, _ = daq_utils.gonio2lab( + prev_coords["x"], + prev_coords["y"], + prev_coords["z"], + current_raw_coords["omega"], + ) + _, yLabCurrent, _, _ = daq_utils.gonio2lab( + current_raw_coords["x"], + current_raw_coords["y"], + current_raw_coords["z"], + current_raw_coords["omega"], + ) + + # Take y co-ordinate from current point and z-coordinate from prev point and transform back to gonio co-ordinates + _, yTweakedCurrent, zTweakedCurrent, _ = daq_utils.lab2gonio( + prev_coords["x"], yLabCurrent, zLabPrev, current_raw_coords["omega"] + ) + return { + "x": current_raw_coords["x"], + "y": yTweakedCurrent, + "z": zTweakedCurrent, + } + + def getVectorObject( + self, prevVectorPoint=None, gonioCoords=None, pen=None, brush=None + ): + """Creates and returns a vector start or end point + + Places a start or end vector marker wherever the crosshair is located in + the sample camera view and returns a dictionary of metadata related to that point + + Args: + prevVectorPoint: Dictionary of metadata related to a point being adjusted. For example, + a previously placed vectorStart point is moved, its old position is used to determine + its new co-ordinates in 3D space + gonioCoords: Dictionary of gonio coordinates. If not provided will retrieve current PV values + pen: QPen object that defines the color of the point's outline + brush: QBrush object that defines the color of the point's fill color + Returns: + A dict mapping the following keys + "coords": A dictionary of tweaked x, y and z positions of the Goniometer + "raw_coords": A dictionary of x, y, z co-ordinates obtained from the Goniometer PVs + "graphicsitem": Qt object referring to the marker on the sample camera + "centerCursorX" and "centerCursorY": Location of the center marker when this marker was placed + """ + if not pen: + pen = QtGui.QPen(QtCore.Qt.blue) + if not brush: + brush = QtGui.QBrush(QtCore.Qt.blue) + markWidth = 10 + # TODO: Place vecMarker in such a way that it matches any arbitrary gonioCoords given to this function + # currently vecMarker will be placed at the center of the sample cam + vecMarker = self.scene.addEllipse( + self.centerMarker.x() + - (markWidth / 2.0) + - 1 + + self.centerMarkerCharOffsetX, + self.centerMarker.y() + - (markWidth / 2.0) + - 1 + + self.centerMarkerCharOffsetY, + markWidth, + markWidth, + pen, + brush, + ) + if not gonioCoords: + gonioCoords = { + "x": self.gon.x.val(), + "y": self.gon.y.val(), + "z": self.gon.z.val(), + "finex": self.gon.cx.val(), + "finey": self.gon.cy.val(), + "omega": self.gon.omega.val(), + } + #if prevVectorPoint: + # vectorCoords = self.transform_vector_coords( + # prevVectorPoint["coords"], gonioCoords + # ) + #else: + vectorCoords = { + k: v for k, v in gonioCoords.items() if k in ["x", "y", "z", "finex", "finey"] + } + logger.info("vector coords: %s" % vectorCoords) + return { + "coords": vectorCoords, + "gonioCoords": gonioCoords, + "graphicsitem": vecMarker, + "centerCursorX": self.centerMarker.x(), + "centerCursorY": self.centerMarker.y(), + } + + def setVectorPointCB(self, pointName): + """Callback function to update a vector point + + Callback to remove or add the appropriate vector start or end point on the sample camera. + Calls getVectorObject to generate metadata related to this point. Draws a vector line if + both start and end is defined + + Args: + point: Point to be placed (Either vectorStart or vectorEnd) + """ + point = getattr(self, pointName) + if point: + self.scene.removeItem(point["graphicsitem"]) + if self.vecLine: + self.scene.removeItem(self.vecLine) + if pointName == "vectorEnd": + brush = QtGui.QBrush(QtCore.Qt.red) + else: + brush = QtGui.QBrush(QtCore.Qt.blue) + point = self.getVectorObject(prevVectorPoint=point, brush=brush) + setattr(self, pointName, point) + if self.vectorStart and self.vectorEnd: + self.drawVector() + + def drawVector(self): + pen = QtGui.QPen(QtCore.Qt.blue) + try: + self.updateVectorLengthAndSpeed() + except: + pass + self.protoVectorRadio.setChecked(True) + self.vecLine = self.scene.addLine( + self.centerMarker.x() + + self.vectorStart["graphicsitem"].x() + + self.centerMarkerCharOffsetX, + self.centerMarker.y() + + self.vectorStart["graphicsitem"].y() + + self.centerMarkerCharOffsetY, + self.centerMarker.x() + + self.vectorEnd["graphicsitem"].x() + + self.centerMarkerCharOffsetX, + self.centerMarker.y() + + self.vectorEnd["graphicsitem"].y() + + self.centerMarkerCharOffsetY, + pen, + ) + self.vecLine.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True) + + def clearVectorCB(self): + if self.vectorStart: + self.scene.removeItem(self.vectorStart["graphicsitem"]) + self.vectorStart = None + if self.vectorEnd: + self.scene.removeItem(self.vectorEnd["graphicsitem"]) + self.vectorEnd = None + self.vecLenLabelOutput.setText("---") + self.vecSpeedLabelOutput.setText("---") + if self.vecLine: + self.scene.removeItem(self.vecLine) + self.vecLine = None + + def puckToDewarCB(self): + while 1: + #self.timerSample.stop() + puckName, ok = PuckDialog.getPuckName() + #self.timerSample.start(SAMPLE_TIMER_DELAY) + if ok: + #self.timerSample.stop() + dewarPos, ok = DewarDialog.getDewarPos(parent=self, action="add") + #self.timerSample.start(SAMPLE_TIMER_DELAY) + if ok and dewarPos is not None and puckName is not None: + ipos = int(dewarPos) + 1 + db_lib.insertIntoContainer( + daq_utils.primaryDewarName, + daq_utils.beamline, + ipos, + db_lib.getContainerIDbyName(puckName, daq_utils.owner), + ) + self.treeChanged_pv.put(1) + else: + break + + def stopRunCB(self): + logger.info("stopping collection") + self.aux_send_to_server("stopDCQueue(1)") + + def stopQueueCB(self): + logger.info("stopping queue") + if self.pauseQueueButton.text() == "Continue": + self.aux_send_to_server("continue_data_collection()") + else: + self.aux_send_to_server("stopDCQueue(2)") + + def mountSampleCB(self): + if getBlConfig("mountEnabled") == 0: + self.popupServerMessage("Mounting disabled!! Call staff!") + return + logger.info("mount selected sample") + self.eraseCB() + if ( + self.dewarTree.getSelectedSample() + ): # If sample ID is not found check the dewartree directly + self.selectedSampleID = self.dewarTree.getSelectedSample() + else: # No sample ID found, do nothing + logger.info("No sample selected, cannot mount") + return + self.send_to_server('mountSample("' + str(self.selectedSampleID) + '")') + self.zoom2Radio.setChecked(True) + self.zoomLevelToggledCB("Zoom2") + self.protoComboBox.setCurrentIndex(self.protoComboBox.findText(str("standard"))) + self.protoComboActivatedCB("standard") + + def unmountSampleCB(self): + logger.info("unmount sample") + self.send_to_server("unmountSample()") + + def refreshCollectionParams(self, selectedSampleRequest, validate_hdf5=True): + reqObj = selectedSampleRequest["request_obj"] + self.protoComboBox.setCurrentIndex( + self.protoComboBox.findText(str(reqObj["protocol"])) + ) + protocol = str(reqObj["protocol"]) + if protocol == "raster": + self.protoRasterRadio.setChecked(True) + elif protocol == "standard": + self.protoStandardRadio.setChecked(True) + elif protocol == "vector": + self.protoVectorRadio.setChecked(True) + else: + self.protoOtherRadio.setChecked(True) + + logger.info("osc range") + self.setGuiValues( + { + "osc_start": reqObj["sweep_start"], + "osc_end": reqObj["sweep_end"] - reqObj["sweep_start"], + "osc_range": reqObj["img_width"], + "exp_time": reqObj["exposure_time"], + "resolution": reqObj["resolution"], + "transmission": reqObj["attenuation"], + } + ) + self.dataPathGB.setFileNumstart_ledit(str(reqObj["file_number_start"])) + self.beamWidth_ledit.setText(str(reqObj["slit_width"])) + self.beamHeight_ledit.setText(str(reqObj["slit_height"])) + if "fastDP" in reqObj: + self.staffScreenDialog.fastDPCheckBox.setChecked( + (reqObj["fastDP"] or reqObj["fastEP"] or reqObj["dimple"]) + ) + if "fastEP" in reqObj: + self.fastEPCheckBox.setChecked(reqObj["fastEP"]) + if "dimple" in reqObj: + self.dimpleCheckBox.setChecked(reqObj["dimple"]) + if "xia2" in reqObj: + self.xia2CheckBox.setChecked(reqObj["xia2"]) + reqObj["energy"] = float(self.energy_ledit.text()) + self.energy_ledit.setText(str(reqObj["energy"])) + energy_s = str(daq_utils.wave2energy(reqObj["wavelength"], digits=6)) + dist_s = str(reqObj["detDist"]) + self.detDistMotorEntry.getEntry().setText(str(dist_s)) + self.dataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) + self.dataPathGB.setBasePath_ledit(str(reqObj["basePath"])) + self.dataPathGB.setDataPath_ledit(str(reqObj["directory"])) + if ( + str(reqObj["protocol"]) == "characterize" + or str(reqObj["protocol"]) == "ednaCol" + ): + prefix_long = ( + str(reqObj["directory"]) + "/ref-" + str(reqObj["file_prefix"]) + ) + else: + prefix_long = str(reqObj["directory"]) + "/" + str(reqObj["file_prefix"]) + fnumstart = reqObj["file_number_start"] + + if ( + str(reqObj["protocol"]) == "characterize" + or str(reqObj["protocol"]) == "ednaCol" + or str(reqObj["protocol"]) == "standard" + or str(reqObj["protocol"]) == "vector" + ): + if "priority" in selectedSampleRequest: + if ( + selectedSampleRequest["priority"] < 0 + and self.staffScreenDialog.albulaDispCheckBox.isChecked() + ): + firstFilename = daq_utils.create_filename(prefix_long, fnumstart) + if validate_hdf5: + if albulaUtils.validate_master_HDF5_file(firstFilename): + albulaUtils.albulaDispFile(firstFilename) + else: + QtWidgets.QMessageBox.information( + self, + "Error", + f"Master HDF5 file {firstFilename} could not be validated", + QtWidgets.QMessageBox.Ok, + ) + self.rasterStepEdit.setText(str(reqObj["gridStep"])) + if reqObj["gridStep"] == self.rasterStepDefs["Coarse"]: + self.rasterGrainCoarseRadio.setChecked(True) + elif reqObj["gridStep"] == self.rasterStepDefs["Fine"]: + self.rasterGrainFineRadio.setChecked(True) + elif reqObj["gridStep"] == self.rasterStepDefs["VFine"]: + self.rasterGrainVFineRadio.setChecked(True) + else: + self.rasterGrainCustomRadio.setChecked(True) + rasterStep = int(reqObj["gridStep"]) + if not self.hideRastersCheckBox.isChecked() and ( + reqObj["protocol"] in ("raster", "stepRaster", "multiCol") + ): + if not self.rasterIsDrawn(selectedSampleRequest): + self.drawPolyRaster(selectedSampleRequest) + self.fillPolyRaster(selectedSampleRequest) + + if ( + str(self.govStateMessagePV.get(as_string=True)) == "state SA" + and self.controlEnabled() # Move only in SA (Any other way for GUI to detect governor state?) + and self.selectedSampleRequest["sample"] # with control enabled + == self.mountedPin_pv.get() + ): # And the sample of the selected request is mounted + self.processSampMove(self.gon.x.val(), "x") + self.processSampMove(self.gon.y.val(), "y") + self.processSampMove(self.gon.z.val(), "z") + if ( + abs( + selectedSampleRequest["request_obj"]["rasterDef"]["omega"] + - self.gon.omega.val() + ) + > 5.0 + ): + comm_s = ( + 'mvaDescriptor("omega",' + + str( + selectedSampleRequest["request_obj"]["rasterDef"]["omega"] + ) + + ")" + ) + self.send_to_server(comm_s) + if str(reqObj["protocol"]) == "eScan": + try: + self.escan_steps_ledit.setText(str(reqObj["steps"])) + self.escan_stepsize_ledit.setText(str(reqObj["stepsize"])) + self.EScanDataPathGB.setBasePath_ledit(reqObj["basePath"]) + self.EScanDataPathGB.setDataPath_ledit(reqObj["directory"]) + self.EScanDataPathGB.setFileNumstart_ledit( + str(reqObj["file_number_start"]) + ) + self.EScanDataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) + self.periodicTable.elementClicked(reqObj["element"]) + except KeyError: + pass + elif ( + str(reqObj["protocol"]) == "characterize" + or str(reqObj["protocol"]) == "ednaCol" + ): + characterizationParams = reqObj["characterizationParams"] + self.characterizeCompletenessEdit.setText( + str(characterizationParams["aimed_completeness"]) + ) + self.characterizeISIGEdit.setText(str(characterizationParams["aimed_ISig"])) + self.characterizeResoEdit.setText( + str(characterizationParams["aimed_resolution"]) + ) + self.characterizeMultiplicityEdit.setText( + str(characterizationParams["aimed_multiplicity"]) + ) + else: # for now, erase the rasters if a non-raster is selected, need to rationalize later + pass + self.showProtParams() + + def row_clicked( + self, index + ): # I need "index" here? seems like I get it from selmod, but sometimes is passed + selmod = self.dewarTree.selectionModel() + selection = selmod.selection() + indexes = selection.indexes() + if len(indexes) == 0: + return + i = 0 + item = self.dewarTree.model.itemFromIndex(indexes[i]) + parent = indexes[i].parent() + try: + puck_name = parent.data() + except AttributeError as e: + logger.error("attribute error in row_clicked: %s", e) + return + itemData = str(item.data(32)) + itemDataType = str(item.data(33)) + self.SelectedItemData = itemData # an attempt to know what is selected and preserve it when refreshing the tree + if itemData == "": + logger.info("nothing there") + return + elif itemDataType == "container": + logger.info("I'm a puck") + return + elif itemDataType == "sample": + self.selectedSampleID = itemData + sample = db_lib.getSampleByID(self.selectedSampleID) + owner = sample["owner"] + sample_name = db_lib.getSampleNamebyID(self.selectedSampleID) + logger.info("sample in pos " + str(itemData)) + if ( + sample["uid"] != self.mountedPin_pv.get() + and getBlConfig("queueCollect") == 0 + ): # Don't fill data paths if an unmounted sample is clicked and queue collect is off + return + if self.osc_start_ledit.text() == "": + self.selectedSampleRequest = daq_utils.createDefaultRequest( + itemData, createVisit=False + ) + self.refreshCollectionParams(self.selectedSampleRequest) + if self.stillModeStatePV.get(): + self.setGuiValues({"osc_range": "0.0"}) + reqObj = self.selectedSampleRequest["request_obj"] + self.dataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) + self.dataPathGB.setBasePath_ledit(reqObj["basePath"]) + self.dataPathGB.setDataPath_ledit(reqObj["directory"]) + self.EScanDataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) + self.EScanDataPathGB.setBasePath_ledit(reqObj["basePath"]) + self.EScanDataPathGB.setDataPath_ledit(reqObj["directory"]) + self.EScanDataPathGB.setFileNumstart_ledit( + str(reqObj["file_number_start"]) + ) + if self.vidActionRasterDefRadio.isChecked(): + self.protoComboBox.setCurrentIndex( + self.protoComboBox.findText(str("raster")) + ) + self.showProtParams() + else: + self.selectedSampleRequest = daq_utils.createDefaultRequest( + itemData, createVisit=False + ) + reqObj = self.selectedSampleRequest["request_obj"] + self.dataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) + self.dataPathGB.setBasePath_ledit(reqObj["basePath"]) + self.dataPathGB.setDataPath_ledit(reqObj["directory"]) + self.EScanDataPathGB.setFilePrefix_ledit(str(reqObj["file_prefix"])) + self.EScanDataPathGB.setBasePath_ledit(reqObj["basePath"]) + self.EScanDataPathGB.setDataPath_ledit(reqObj["directory"]) + self.EScanDataPathGB.setFileNumstart_ledit( + str(reqObj["file_number_start"]) + ) + else: # request + self.selectedSampleRequest = db_lib.getRequestByID(itemData) + reqObj = self.selectedSampleRequest["request_obj"] + reqID = self.selectedSampleRequest["uid"] + self.selectedSampleID = self.selectedSampleRequest["sample"] + sample = db_lib.getSampleByID(self.selectedSampleID) + if ( + sample["uid"] != self.mountedPin_pv.get() + and getBlConfig("queueCollect") == 0 + ): # Don't fill request data if unmounted sample and queuecollect is off + return + owner = sample["owner"] + if reqObj["protocol"] == "eScan": + try: + if reqObj["runChooch"]: + resultList = db_lib.getResultsforRequest(reqID) + if len(resultList) > 0: + lastResult = resultList[-1] + if ( + db_lib.getResult(lastResult["uid"])["result_type"] + == "choochResult" + ): + resultID = lastResult["uid"] + logger.info("plotting chooch") + self.processChoochResult(resultID) + except KeyError: + logger.error( + "KeyError - ignoring chooch-related items, perhaps from a bad energy scan" + ) + self.refreshCollectionParams(self.selectedSampleRequest) + + def processXrecRasterCB(self, value=None, char_value=None, **kw): + xrecFlag = value + if xrecFlag != "0": + self.xrecRasterSignal.emit(xrecFlag) + + def processChoochResultsCB(self, value=None, char_value=None, **kw): + choochFlag = value + if choochFlag != "0": + self.choochResultSignal.emit(choochFlag) + + def processEnergyChangeCB(self, value=None, char_value=None, **kw): + energyVal = value + self.energyChangeSignal.emit(energyVal) + + def mountedPinChangedCB(self, value=None, char_value=None, **kw): + mountedPinPos = value + self.mountedPinSignal.emit(mountedPinPos) + + def beamSizeChangedCB(self, value=None, char_value=None, **kw): + beamSizeFlag = value + self.beamSizeSignal.emit(beamSizeFlag) + + def controlMasterChangedCB(self, value=None, char_value=None, **kw): + controlMasterPID = value + self.controlMasterSignal.emit(controlMasterPID) + + def zebraArmStateChangedCB(self, value=None, char_value=None, **kw): + armState = value + self.zebraArmStateSignal.emit(armState) + + def govRobotSeReachChangedCB(self, value=None, char_value=None, **kw): + armState = value + self.govRobotSeReachSignal.emit(armState) + + def govRobotSaReachChangedCB(self, value=None, char_value=None, **kw): + armState = value + self.govRobotSaReachSignal.emit(armState) + + def govRobotDaReachChangedCB(self, value=None, char_value=None, **kw): + armState = value + self.govRobotDaReachSignal.emit(armState) + + def govRobotBlReachChangedCB(self, value=None, char_value=None, **kw): + armState = value + self.govRobotBlReachSignal.emit(armState) + + def detMessageChangedCB(self, value=None, char_value=None, **kw): + state = char_value + self.detMessageSignal.emit(state) + + def sampleFluxChangedCB(self, value=None, char_value=None, **kw): + state = value + self.sampleFluxSignal.emit(state) + + def zebraPulseStateChangedCB(self, value=None, char_value=None, **kw): + state = value + self.zebraPulseStateSignal.emit(state) + + def stillModeStateChangedCB(self, value=None, char_value=None, **kw): + state = value + self.stillModeStateSignal.emit(state) + + def zebraDownloadStateChangedCB(self, value=None, char_value=None, **kw): + state = value + self.zebraDownloadStateSignal.emit(state) + + def zebraSentTriggerStateChangedCB(self, value=None, char_value=None, **kw): + state = value + self.zebraSentTriggerStateSignal.emit(state) + + def zebraReturnedTriggerStateChangedCB(self, value=None, char_value=None, **kw): + state = value + self.zebraReturnedTriggerStateSignal.emit(state) + + def shutterChangedCB(self, value=None, char_value=None, **kw): + shutterVal = value + self.fastShutterSignal.emit(shutterVal) + + def gripTempChangedCB(self, value=None, char_value=None, **kw): + gripVal = value + self.gripTempSignal.emit(gripVal) + + def cryostreamTempChangedCB(self, value=None, char_value=None, **kw): + cryostreamTemp = value + self.cryostreamTempSignal.emit(cryostreamTemp) + + def ringCurrentChangedCB(self, value=None, char_value=None, **kw): + ringCurrentVal = value + self.ringCurrentSignal.emit(ringCurrentVal) + + def beamAvailableChangedCB(self, value=None, char_value=None, **kw): + beamAvailableVal = value + self.beamAvailableSignal.emit(beamAvailableVal) + + def sampleExposedChangedCB(self, value=None, char_value=None, **kw): + sampleExposedVal = value + self.sampleExposedSignal.emit(sampleExposedVal) + + def processSampMoveCB(self, value=None, char_value=None, **kw): + posRBV = value + motID = kw["motID"] + self.sampMoveSignal.emit(posRBV, motID) + + def processROIChangeCB(self, value=None, char_value=None, **kw): + posRBV = value + ID = kw["ID"] + self.roiChangeSignal.emit(posRBV, ID) + + def processHighMagCursorChangeCB(self, value=None, char_value=None, **kw): + posRBV = value + ID = kw["ID"] + self.highMagCursorChangeSignal.emit(posRBV, ID) + + def processLowMagCursorChangeCB(self, value=None, char_value=None, **kw): + posRBV = value + ID = kw["ID"] + self.lowMagCursorChangeSignal.emit(posRBV, ID) + + def treeChangedCB(self, value=None, char_value=None, **kw): + if self.processID != self.treeChanged_pv.get(): + self.refreshTreeSignal.emit() + + def serverMessageCB(self, value=None, char_value=None, **kw): + serverMessageVar = char_value + self.serverMessageSignal.emit(serverMessageVar) + + def serverPopupMessageCB(self, value=None, char_value=None, **kw): + serverMessageVar = char_value + self.serverPopupMessageSignal.emit(serverMessageVar) + + def programStateCB(self, value=None, char_value=None, **kw): + programStateVar = value + self.programStateSignal.emit(programStateVar) + + def pauseButtonStateCB(self, value=None, char_value=None, **kw): + pauseButtonStateVar = value + self.pauseButtonStateSignal.emit(pauseButtonStateVar) + + def initOphyd(self): + if daq_utils.beamline == "nyx": + # initialize devices + self.gon = GonioDevice("XF:19IDC-ES{MD2}:", name="gonio") + self.camera = CameraDevice("XF:19IDC-ES{MD2}:", name="camera") + self.md2 = MD2Device("XF:19IDC-ES{MD2}:", name="md2") + self.front_light = LightDevice("XF:19IDC-ES{MD2}:Front", name="front_light") + self.back_light = LightDevice("XF:19IDC-ES{MD2}:Back", name="back_light") + self.aperture = MD2ApertureDevice("XF:19IDC-ES{MD2}:", name="aperture") + else: + pass + + def initUI(self): + self.tabs = QtWidgets.QTabWidget() + self.comm_pv = PV(daq_utils.beamlineComm + "command_s") + self.immediate_comm_pv = PV(daq_utils.beamlineComm + "immediate_command_s") + self.stillModeStatePV = PV(daq_utils.pvLookupDict["stillModeStatus"]) + self.progressDialog = QtWidgets.QProgressDialog() + self.progressDialog.setCancelButtonText("Cancel") + self.progressDialog.setModal(False) + self.progressDialog.reset() + tab1 = QtWidgets.QWidget() + vBoxlayout1 = QtWidgets.QVBoxLayout() + splitter1 = QtWidgets.QSplitter(QtCore.Qt.Vertical, self) + splitter1.addWidget(self.tabs) + self.setCentralWidget(splitter1) + splitterSizes = [600, 100] + importAction = QtWidgets.QAction("Import Spreadsheet...", self) + importAction.triggered.connect(self.popImportDialogCB) + modeGroup = QtWidgets.QActionGroup(self) + modeGroup.setExclusive(True) + self.userAction = QtWidgets.QAction("User Mode", self, checkable=True) + self.userAction.triggered.connect(self.setUserModeCB) + self.userAction.setChecked(True) + self.expertAction = QtWidgets.QAction("Expert Mode", self, checkable=True) + self.expertAction.triggered.connect(self.setExpertModeCB) + self.staffAction = QtWidgets.QAction("Staff Panel...", self) + self.staffAction.triggered.connect(self.popStaffDialogCB) + modeGroup.addAction(self.userAction) + modeGroup.addAction(self.expertAction) + exitAction = QtWidgets.QAction(QtGui.QIcon("exit24.png"), "Exit", self) + exitAction.setShortcut("Ctrl+Q") + exitAction.setStatusTip("Exit application") + exitAction.triggered.connect(self.closeAll) + self.statusBar() + self.queue_collect_status_widget = QtWidgets.QLabel("Queue Collect: ON") + self.statusBar().addPermanentWidget(self.queue_collect_status_widget) + menubar = self.menuBar() + fileMenu = menubar.addMenu("&File") + settingsMenu = menubar.addMenu("Settings") + fileMenu.addAction(importAction) + fileMenu.addAction(self.userAction) + fileMenu.addAction(self.expertAction) + fileMenu.addAction(self.staffAction) + # Define all of the available actions for the overlay color group + self.BlueOverlayAction = QtWidgets.QAction("Blue", self, checkable=True) + self.RedOverlayAction = QtWidgets.QAction("Red", self, checkable=True) + self.GreenOverlayAction = QtWidgets.QAction("Green", self, checkable=True) + self.WhiteOverlayAction = QtWidgets.QAction("White", self, checkable=True) + self.BlackOverlayAction = QtWidgets.QAction("Black", self, checkable=True) + # Connect all of the trigger callbacks to their respective actions + self.BlueOverlayAction.triggered.connect(self.blueOverlayTriggeredCB) + self.RedOverlayAction.triggered.connect(self.redOverlayTriggeredCB) + self.GreenOverlayAction.triggered.connect(self.greenOverlayTriggeredCB) + self.WhiteOverlayAction.triggered.connect(self.whiteOverlayTriggeredCB) + self.BlackOverlayAction.triggered.connect(self.blackOverlayTriggeredCB) + # Create the action group and populate it + self.overlayColorActionGroup = QtWidgets.QActionGroup(self) + self.overlayColorActionGroup.setExclusive(True) + self.overlayColorActionGroup.addAction(self.BlueOverlayAction) + self.overlayColorActionGroup.addAction(self.RedOverlayAction) + self.overlayColorActionGroup.addAction(self.GreenOverlayAction) + self.overlayColorActionGroup.addAction(self.WhiteOverlayAction) + self.overlayColorActionGroup.addAction(self.BlackOverlayAction) + # Create the menu item with the submenu, add the group + self.overlayMenu = settingsMenu.addMenu("Overlay Settings") + self.overlayMenu.addActions(self.overlayColorActionGroup.actions()) + try: + if getBlConfig("defaultOverlayColor") == "GREEN": + self.GreenOverlayAction.setChecked(True) + else: + self.BlueOverlayAction.setChecked(True) + except KeyError as e: + logger.warning("No value for defaultOverlayColor") + self.BlueOverlayAction.setChecked(True) + + fileMenu.addAction(exitAction) + self.setGeometry(300, 300, 1550, 1000) # width and height here. + self.setWindowTitle("LSDC on %s" % daq_utils.beamline) + self.show() + + def blueOverlayTriggeredCB(self): + overlayBrush = QtGui.QBrush(QtCore.Qt.blue) + self.centerMarker.setBrush(overlayBrush) + self.imageScale.setPen(QtGui.QPen(overlayBrush, 2.0)) + self.imageScaleText.setPen(QtGui.QPen(overlayBrush, 1.0)) + + def redOverlayTriggeredCB(self): + overlayBrush = QtGui.QBrush(QtCore.Qt.red) + self.centerMarker.setBrush(overlayBrush) + self.imageScale.setPen(QtGui.QPen(overlayBrush, 2.0)) + self.imageScaleText.setPen(QtGui.QPen(overlayBrush, 1.0)) + + def greenOverlayTriggeredCB(self): + overlayBrush = QtGui.QBrush(QtCore.Qt.green) + self.centerMarker.setBrush(overlayBrush) + self.imageScale.setPen(QtGui.QPen(overlayBrush, 2.0)) + self.imageScaleText.setPen(QtGui.QPen(overlayBrush, 1.0)) + + def whiteOverlayTriggeredCB(self): + overlayBrush = QtGui.QBrush(QtCore.Qt.white) + self.centerMarker.setBrush(overlayBrush) + self.imageScale.setPen(QtGui.QPen(overlayBrush, 2.0)) + self.imageScaleText.setPen(QtGui.QPen(overlayBrush, 1.0)) + + def blackOverlayTriggeredCB(self): + overlayBrush = QtGui.QBrush(QtCore.Qt.black) + self.centerMarker.setBrush(overlayBrush) + self.imageScale.setPen(QtGui.QPen(overlayBrush, 2.0)) + self.imageScaleText.setPen(QtGui.QPen(overlayBrush, 1.0)) + + def popStaffDialogCB(self): + if self.controlEnabled(): + self.staffScreenDialog.show() + else: + self.popupServerMessage("You don't have control") + + def closeAll(self): + self.hutchCornerCamThread.stop() + self.hutchTopCamThread.stop() + self.hutchCornerCamThread.wait() + self.hutchTopCamThread.wait() + QtWidgets.QApplication.instance().quit() + + def initCallbacks(self): + self.beamSizeSignal.connect(self.processBeamSize) + self.beamSize_pv.add_callback(self.beamSizeChangedCB) + + self.treeChanged_pv = PV(daq_utils.beamlineComm + "live_q_change_flag") + self.refreshTreeSignal.connect(self.dewarTree.refreshTree) + self.treeChanged_pv.add_callback(self.treeChangedCB) + self.mountedPin_pv = PV(daq_utils.beamlineComm + "mounted_pin") + self.mountedPinSignal.connect(self.processMountedPin) + self.mountedPin_pv.add_callback(self.mountedPinChangedCB) + det_stop_pv = daq_utils.pvLookupDict["stopEiger"] + logger.info("setting stop Eiger detector PV: %s" % det_stop_pv) + self.stopDet_pv = PV(det_stop_pv) + det_reboot_pv = daq_utils.pvLookupDict["eigerIOC_reboot"] + logger.info("setting detector ioc reboot PV: %s" % det_reboot_pv) + self.rebootDetIOC_pv = PV(det_reboot_pv) + rz_pv = daq_utils.pvLookupDict["zebraReset"] + logger.info("setting zebra reset PV: %s" % rz_pv) + self.resetZebra_pv = PV(rz_pv) + rz_reboot_pv = daq_utils.pvLookupDict["zebraRebootIOC"] + logger.info("setting zebra reboot ioc PV: %s" % rz_reboot_pv) + self.rebootZebraIOC_pv = PV(rz_reboot_pv) + self.zebraArmedPV = PV(daq_utils.pvLookupDict["zebraArmStatus"]) + self.zebraArmStateSignal.connect(self.processZebraArmState) + self.zebraArmedPV.add_callback(self.zebraArmStateChangedCB) + + self.govRobotSeReachPV = PV(daq_utils.pvLookupDict["govRobotSeReach"]) + self.govRobotSeReachSignal.connect(self.processGovRobotSeReach) + self.govRobotSeReachPV.add_callback(self.govRobotSeReachChangedCB) + + self.govRobotSaReachPV = PV(daq_utils.pvLookupDict["govRobotSaReach"]) + self.govRobotSaReachSignal.connect(self.processGovRobotSaReach) + self.govRobotSaReachPV.add_callback(self.govRobotSaReachChangedCB) + + self.govRobotDaReachPV = PV(daq_utils.pvLookupDict["govRobotDaReach"]) + self.govRobotDaReachSignal.connect(self.processGovRobotDaReach) + self.govRobotDaReachPV.add_callback(self.govRobotDaReachChangedCB) + + self.govRobotBlReachPV = PV(daq_utils.pvLookupDict["govRobotBlReach"]) + self.govRobotBlReachSignal.connect(self.processGovRobotBlReach) + self.govRobotBlReachPV.add_callback(self.govRobotBlReachChangedCB) + + self.detectorMessagePV = PV(daq_utils.pvLookupDict["eigerStatMessage"]) + self.detMessageSignal.connect(self.processDetMessage) + self.detectorMessagePV.add_callback(self.detMessageChangedCB) + + self.sampleFluxSignal.connect(self.processSampleFlux) + self.sampleFluxPV.add_callback(self.sampleFluxChangedCB) + + self.stillModeStateSignal.connect(self.processStillModeState) + self.stillModeStatePV.add_callback(self.stillModeStateChangedCB) + + self.zebraPulsePV = PV(daq_utils.pvLookupDict["zebraPulseStatus"]) + self.zebraPulseStateSignal.connect(self.processZebraPulseState) + self.zebraPulsePV.add_callback(self.zebraPulseStateChangedCB) + + self.zebraDownloadPV = PV(daq_utils.pvLookupDict["zebraDownloading"]) + self.zebraDownloadStateSignal.connect(self.processZebraDownloadState) + self.zebraDownloadPV.add_callback(self.zebraDownloadStateChangedCB) + + self.zebraSentTriggerPV = PV(daq_utils.pvLookupDict["zebraSentTriggerStatus"]) + self.zebraSentTriggerStateSignal.connect(self.processZebraSentTriggerState) + self.zebraSentTriggerPV.add_callback(self.zebraSentTriggerStateChangedCB) + + self.zebraReturnedTriggerPV = PV( + daq_utils.pvLookupDict["zebraTriggerReturnStatus"] + ) + self.zebraReturnedTriggerStateSignal.connect( + self.processZebraReturnedTriggerState + ) + self.zebraReturnedTriggerPV.add_callback( + self.zebraReturnedTriggerStateChangedCB + ) + + self.controlMaster_pv = PV(daq_utils.beamlineComm + "zinger_flag") + self.controlMasterSignal.connect(self.processControlMaster) + self.controlMaster_pv.add_callback(self.controlMasterChangedCB) + + self.beamCenterX_pv = PV(daq_utils.pvLookupDict["beamCenterX"]) + self.beamCenterY_pv = PV(daq_utils.pvLookupDict["beamCenterY"]) + + self.choochResultFlag_pv = PV(daq_utils.beamlineComm + "choochResultFlag") + self.choochResultSignal.connect(self.processChoochResult) + self.choochResultFlag_pv.add_callback(self.processChoochResultsCB) + self.xrecRasterFlag_pv = PV(daq_utils.beamlineComm + "xrecRasterFlag") + self.xrecRasterFlag_pv.put("0") + self.xrecRasterSignal.connect(self.displayXrecRaster) + self.xrecRasterFlag_pv.add_callback(self.processXrecRasterCB) + self.message_string_pv = PV(daq_utils.beamlineComm + "message_string") + self.serverMessageSignal.connect(self.printServerMessage) + self.message_string_pv.add_callback(self.serverMessageCB) + self.popup_message_string_pv = PV( + daq_utils.beamlineComm + "gui_popup_message_string" + ) + self.serverPopupMessageSignal.connect(self.popupServerMessage) + self.popup_message_string_pv.add_callback(self.serverPopupMessageCB) + self.program_state_pv = PV(daq_utils.beamlineComm + "program_state") + self.programStateSignal.connect(self.colorProgramState) + self.program_state_pv.add_callback(self.programStateCB) + self.pause_button_state_pv = PV(daq_utils.beamlineComm + "pause_button_state") + self.pauseButtonStateSignal.connect(self.changePauseButtonState) + self.pause_button_state_pv.add_callback(self.pauseButtonStateCB) + + self.energyChangeSignal.connect(self.processEnergyChange) + self.energy_pv.add_callback(self.processEnergyChangeCB, motID="x") + + self.sampx_pv = PV(self.gon.x.readback.pvname) + self.sampMoveSignal.connect(self.processSampMove) + self.sampx_pv.add_callback(self.processSampMoveCB, motID="x") + self.sampy_pv = PV(self.gon.y.readback.pvname) + self.sampy_pv.add_callback(self.processSampMoveCB, motID="y") + self.sampz_pv = PV(self.gon.z.readback.pvname) + self.sampz_pv.add_callback(self.processSampMoveCB, motID="z") + + if self.scannerType == "PI": + self.sampFineX_pv = PV(daq_utils.motor_dict["fineX"] + ".RBV") + self.sampFineX_pv.add_callback(self.processSampMoveCB, motID="fineX") + self.sampFineY_pv = PV(daq_utils.motor_dict["fineY"] + ".RBV") + self.sampFineY_pv.add_callback(self.processSampMoveCB, motID="fineY") + self.sampFineZ_pv = PV(daq_utils.motor_dict["fineZ"] + ".RBV") + self.sampFineZ_pv.add_callback(self.processSampMoveCB, motID="fineZ") + + self.omega_pv = PV(self.gon.omega.setpoint.pvname) + self.omegaTweak_pv = PV(self.gon.omega.setpoint.pvname) + self.sampyTweak_pv = PV(self.gon.y.setpoint.pvname) + #if daq_utils.beamline == "nyx": + # self.sampzTweak_pv = PV(self.gon.x.setpoint.pvname + ".RLV") + #else: + self.sampzTweak_pv = PV(self.gon.z.setpoint.pvname) + self.omegaRBV_pv = PV(self.gon.omega.readback.pvname) + self.omegaRBV_pv.add_callback( + self.processSampMoveCB, motID="omega" + ) # I think monitoring this allows for the textfield to monitor val and this to deal with the graphics. Else next line has two callbacks on same thing. + self.photonShutterOpen_pv = PV(daq_utils.pvLookupDict["photonShutterOpen"]) + self.photonShutterClose_pv = PV(daq_utils.pvLookupDict["photonShutterClose"]) + self.fastShutterRBV_pv = PV(daq_utils.motor_dict["fastShutter"] + ".RBV") + self.fastShutterSignal.connect(self.processFastShutter) + self.fastShutterRBV_pv.add_callback(self.shutterChangedCB) + self.gripTempSignal.connect(self.processGripTemp) + self.gripTemp_pv.add_callback(self.gripTempChangedCB) + if getBlConfig(CRYOSTREAM_ONLINE): + self.cryostreamTempSignal.connect(self.processCryostreamTemp) + self.cryostreamTemp_pv.add_callback(self.cryostreamTempChangedCB) + self.ringCurrentSignal.connect(self.processRingCurrent) + self.ringCurrent_pv.add_callback(self.ringCurrentChangedCB) + self.beamAvailableSignal.connect(self.processBeamAvailable) + self.beamAvailable_pv.add_callback(self.beamAvailableChangedCB) + self.sampleExposedSignal.connect(self.processSampleExposed) + self.sampleExposed_pv.add_callback(self.sampleExposedChangedCB) + self.highMagCursorChangeSignal.connect(self.processHighMagCursorChange) + self.highMagCursorX_pv.add_callback(self.processHighMagCursorChangeCB, ID="x") + self.highMagCursorY_pv.add_callback(self.processHighMagCursorChangeCB, ID="y") + self.lowMagCursorChangeSignal.connect(self.processLowMagCursorChange) + self.lowMagCursorX_pv.add_callback(self.processLowMagCursorChangeCB, ID="x") + self.lowMagCursorY_pv.add_callback(self.processLowMagCursorChangeCB, ID="y") + + def popupServerMessage(self, message_s): + if self.popUpMessageInit: + self.popUpMessageInit = 0 + return + self.popupMessage.done(1) + if message_s == "killMessage": + return + else: + self.popupMessage.showMessage(message_s) + + def printServerMessage(self, message_s): + if self.textWindowMessageInit: + self.textWindowMessageInit = 0 + return + logger.info(message_s) + print(message_s) + + def colorProgramState(self, programState_s): + if programState_s.find("Ready") == -1: + self.statusLabel.setColor("yellow") + else: + self.statusLabel.setColor("#99FF66") + + def changePauseButtonState(self, buttonState_s): + self.pauseQueueButton.setText(buttonState_s) + if buttonState_s.find("Pause") != -1: + self.pauseQueueButton.setStyleSheet("background-color: None") + else: + self.pauseQueueButton.setStyleSheet("background-color: yellow") + + def controlEnabled(self): + return ( + self.processID == abs(int(self.controlMaster_pv.get())) + and self.controlMasterCheckBox.isChecked() + ) + + def send_to_server(self, s): + if s == "lockControl": + self.controlMaster_pv.put(0 - self.processID) + return + if s == "unlockControl": + self.controlMaster_pv.put(self.processID) + return + if self.controlEnabled(): + time.sleep(0.01) + logger.info("send_to_server: %s" % s) + self.comm_pv.put(s) + else: + self.popupServerMessage("You don't have control") + + def aux_send_to_server(self, s): + if self.controlEnabled(): + time.sleep(0.01) + logger.info("aux_send_to_server: %s" % s) + self.immediate_comm_pv.put(s) + else: + self.popupServerMessage("You don't have control") diff --git a/gui/data_loc_info.py b/gui/data_loc_info.py new file mode 100644 index 00000000..999c1839 --- /dev/null +++ b/gui/data_loc_info.py @@ -0,0 +1,113 @@ +import logging +import os +import typing + +from qtpy import QtCore, QtGui, QtWidgets + +import daq_utils +import db_lib +from config_params import VALID_PREFIX_LENGTH, VALID_PREFIX_NAME + +if typing.TYPE_CHECKING: + from lsdcGui import ControlMain + +logger = logging.getLogger() + + +class DataLocInfo(QtWidgets.QGroupBox): + def __init__(self, parent: "ControlMain"): + QtWidgets.QGroupBox.__init__(self, parent) + self.parent = parent + self.setTitle("Data Location") + self.vBoxDPathParams1 = QtWidgets.QVBoxLayout() + self.hBoxDPathParams1 = QtWidgets.QHBoxLayout() + self.basePathLabel = QtWidgets.QLabel("Base Path:") + self.base_path_ledit = QtWidgets.QLabel() + self.base_path_ledit.setText(daq_utils.getBlConfig("visitDirectory")) + self.base_path_ledit.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) + # self.base_path_ledit.textChanged[str].connect(self.basePathTextChanged) + self.browseBasePathButton = QtWidgets.QPushButton("Browse...") + self.browseBasePathButton.setEnabled(False) + # self.browseBasePathButton.clicked.connect(self.parent.popBaseDirectoryDialogCB) + self.hBoxDPathParams1.addWidget(self.basePathLabel) + self.hBoxDPathParams1.addWidget(self.base_path_ledit) + self.hBoxDPathParams1.addWidget(self.browseBasePathButton) + self.hBoxDPathParams2 = QtWidgets.QHBoxLayout() + self.dataPrefixLabel = QtWidgets.QLabel( + "Data Prefix:\n(%s Char Limit)" % VALID_PREFIX_LENGTH + ) + self.prefix_ledit = QtWidgets.QLineEdit() + self.prefix_ledit.textChanged[str].connect(self.prefixTextChanged) + self.prefix_ledit.setValidator( + QtGui.QRegExpValidator(QtCore.QRegExp(VALID_PREFIX_NAME), self.prefix_ledit) + ) + self.hBoxDPathParams2.addWidget(self.dataPrefixLabel) + self.hBoxDPathParams2.addWidget(self.prefix_ledit) + self.dataNumstartLabel = QtWidgets.QLabel("File Number Start:") + self.file_numstart_ledit = QtWidgets.QLineEdit() + self.file_numstart_ledit.setValidator(QtGui.QIntValidator(1, 99999, self)) + self.file_numstart_ledit.setFixedWidth(50) + self.hBoxDPathParams3 = QtWidgets.QHBoxLayout() + self.dataPathLabel = QtWidgets.QLabel("Data Path:") + self.dataPath_ledit = QtWidgets.QLineEdit() + self.dataPath_ledit.setFrame(False) + self.dataPath_ledit.setReadOnly(True) + self.hBoxDPathParams3.addWidget(self.dataPathLabel) + self.hBoxDPathParams3.addWidget(self.dataPath_ledit) + self.hBoxDPathParams2.addWidget(self.dataNumstartLabel) + self.hBoxDPathParams2.addWidget(self.file_numstart_ledit) + self.vBoxDPathParams1.addLayout(self.hBoxDPathParams1) + self.vBoxDPathParams1.addLayout(self.hBoxDPathParams2) + self.vBoxDPathParams1.addLayout(self.hBoxDPathParams3) + self.setLayout(self.vBoxDPathParams1) + + def basePathTextChanged(self, text): + prefix = self.prefix_ledit.text() + self.setDataPath_ledit( + text + "/" + str(daq_utils.getVisitName()) + "/" + prefix + "/#/" + ) + + def prefixTextChanged(self, text): + prefix = self.prefix_ledit.text() + try: + runNum = db_lib.getSampleRequestCount(self.parent.selectedSampleID) + except KeyError: + logger.error("just setting a value of 1 for now") + runNum = 1 + try: + ( + puckPosition, + samplePositionInContainer, + containerID, + ) = db_lib.getCoordsfromSampleID( + daq_utils.beamline, self.parent.selectedSampleID + ) + except IndexError: + logger.error("IndexError returning") + return + self.setDataPath_ledit( + self.base_path_ledit.text() + + "/" + + str(daq_utils.getVisitName()) + + "/" + + prefix + + "/" + + str(runNum + 1) + + "/" + + db_lib.getContainerNameByID(containerID) + + "_" + + str(samplePositionInContainer + 1) + + "/" + ) + + def setFileNumstart_ledit(self, s): + self.file_numstart_ledit.setText(s) + + def setFilePrefix_ledit(self, s): + self.prefix_ledit.setText(s) + + def setBasePath_ledit(self, s): + self.base_path_ledit.setText(s) + + def setDataPath_ledit(self, s): + self.dataPath_ledit.setText(s) diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py new file mode 100644 index 00000000..b0c05f9c --- /dev/null +++ b/gui/dewar_tree.py @@ -0,0 +1,481 @@ +import logging +import typing + +from qtpy import QtCore, QtGui, QtWidgets +from qtpy.QtCore import Qt + +import daq_utils +import db_lib +from config_params import DEWAR_SECTORS, PUCKS_PER_DEWAR_SECTOR, SAMPLE_TIMER_DELAY + +if typing.TYPE_CHECKING: + from lsdcGui import ControlMain + +logger = logging.getLogger() + +global sampleNameDict +sampleNameDict = {} + +global containerDict +containerDict = {} + + +class DewarTree(QtWidgets.QTreeView): + def __init__(self, parent: "ControlMain"): + super(DewarTree, self).__init__(parent) + self.pucksPerDewarSector = PUCKS_PER_DEWAR_SECTOR[daq_utils.beamline] + self.dewarSectors = DEWAR_SECTORS[daq_utils.beamline] + self.parent = parent + self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) + self.setAnimated(True) + self.model = QtGui.QStandardItemModel() + self.model.itemChanged.connect(self.queueSelectedSample) + # self.isExpanded = 1 + self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self.openMenu) + + def openMenu(self, position): + indexes = self.selectedIndexes() + selectedLevels = set() + if indexes: + for index in indexes: + level = 0 + while index.parent().isValid(): + index = index.parent() + level += 1 + selectedLevels.add(level) + if len(selectedLevels) == 1: + level = list(selectedLevels)[0] + menu = QtWidgets.QMenu() + if level == 2: # This is usually a request + deleteReqAction = QtWidgets.QAction( + "Delete selected request(s)", self + ) + deleteReqAction.triggered.connect(self.deleteSelectedCB) + cloneReqAction = QtWidgets.QAction("Clone selected request", self) + cloneReqAction.triggered.connect(self.cloneRequestCB) + queueSelAction = QtWidgets.QAction( + "Queue selected request(s)", self + ) + queueSelAction.triggered.connect(self.queueAllSelectedCB) + dequeueSelAction = QtWidgets.QAction( + "Dequeue selected request(s)", self + ) + dequeueSelAction.triggered.connect(self.deQueueAllSelectedCB) + menu.addAction(cloneReqAction) + menu.addAction(queueSelAction) + menu.addAction(dequeueSelAction) + menu.addSeparator() + menu.addAction(deleteReqAction) + menu.exec_(self.viewport().mapToGlobal(position)) + + def cloneRequestCB(self): + # Only the first selected request is cloned (If multiple are chosen) + index = self.selectedIndexes()[0] + item = self.model.itemFromIndex(index) + requestData = db_lib.getRequestByID(item.data(32)) + protocol = requestData["request_obj"]["protocol"] + if "raster" in protocol.lower(): # Will cover specRaster and stepRaster as well + self.parent.cloneRequestCB() + elif "standard" in protocol.lower(): + self.parent.addRequestsToAllSelectedCB() + + def keyPressEvent(self, event): + if event.key() == Qt.Key_Delete or event.key() == Qt.Key_Backspace: + self.deleteSelectedCB(0) + else: + super(DewarTree, self).keyPressEvent(event) + + def refreshTree(self): + self.parent.dewarViewToggleCheckCB() + + def refreshTreeDewarView(self): + selectedIndex = None + mountedIndex = None + selectedSampleIndex = None + puck = "" + collectionRunning = False + self.model.clear() + dewarContents = db_lib.getContainerByName( + daq_utils.primaryDewarName, daq_utils.beamline + )["content"] + for i in range(0, len(dewarContents)): # dewar contents is the list of puck IDs + parentItem = self.model.invisibleRootItem() + if dewarContents[i] == "": + puck = "" + puckName = "" + else: + if dewarContents[i] not in containerDict: + puck = db_lib.getContainerByID(dewarContents[i]) + containerDict[dewarContents[i]] = puck + else: + puck = containerDict[dewarContents[i]] + puckName = puck["name"] + index_s = "%d%s" % ( + (i) / self.pucksPerDewarSector + 1, + chr(((i) % self.pucksPerDewarSector) + ord("A")), + ) + item = QtGui.QStandardItem( + QtGui.QIcon(":/trolltech/styles/commonstyle/images/file-16.png"), + index_s + " " + puckName, + ) + item.setData(puckName, 32) + item.setData("container", 33) + parentItem.appendRow(item) + parentItem = item + if puck != "" and puckName != "private": + puckContents = puck["content"] + puckSize = len(puckContents) + for j in range(0, len(puckContents)): # should be the list of samples + if puckContents[j] != "": + if puckContents[j] not in sampleNameDict: + sampleName = db_lib.getSampleNamebyID(puckContents[j]) + sampleNameDict[puckContents[j]] = sampleName + else: + sampleName = sampleNameDict[puckContents[j]] + position_s = str(j + 1) + "-" + sampleName + item = QtGui.QStandardItem( + QtGui.QIcon( + ":/trolltech/styles/commonstyle/images/file-16.png" + ), + position_s, + ) + item.setData( + puckContents[j], 32 + ) # just stuck sampleID there, but negate it to diff from reqID + item.setData("sample", 33) + if puckContents[j] == self.parent.mountedPin_pv.get(): + item.setForeground(QtGui.QColor("red")) + font = QtGui.QFont() + font.setItalic(True) + font.setOverline(True) + font.setUnderline(True) + item.setFont(font) + parentItem.appendRow(item) + if puckContents[j] == self.parent.mountedPin_pv.get(): + mountedIndex = self.model.indexFromItem(item) + if ( + puckContents[j] == self.parent.selectedSampleID + ): # looking for the selected item + logger.info("found " + str(self.parent.SelectedItemData)) + selectedSampleIndex = self.model.indexFromItem(item) + sampleRequestList = db_lib.getRequestsBySampleID( + puckContents[j] + ) + for k in range(len(sampleRequestList)): + if not ("protocol" in sampleRequestList[k]["request_obj"]): + continue + col_item = QtGui.QStandardItem( + QtGui.QIcon( + ":/trolltech/styles/commonstyle/images/file-16.png" + ), + sampleRequestList[k]["request_obj"]["file_prefix"] + + "_" + + sampleRequestList[k]["request_obj"]["protocol"], + ) + col_item.setData(sampleRequestList[k]["uid"], 32) + col_item.setData("request", 33) + col_item.setFlags( + Qt.ItemIsUserCheckable + | Qt.ItemIsEnabled + | Qt.ItemIsEditable + | Qt.ItemIsSelectable + ) + if sampleRequestList[k]["priority"] == 99999: + col_item.setCheckState(Qt.Checked) + col_item.setBackground(QtGui.QColor("green")) + selectedIndex = self.model.indexFromItem( + col_item + ) ##attempt to leave it on the request after collection + + collectionRunning = True + self.parent.refreshCollectionParams( + sampleRequestList[k], validate_hdf5=False + ) + elif sampleRequestList[k]["priority"] > 0: + col_item.setCheckState(Qt.Checked) + col_item.setBackground(QtGui.QColor("white")) + elif sampleRequestList[k]["priority"] < 0: + col_item.setCheckable(False) + col_item.setBackground(QtGui.QColor("cyan")) + else: + col_item.setCheckState(Qt.Unchecked) + col_item.setBackground(QtGui.QColor("white")) + item.appendRow(col_item) + if ( + sampleRequestList[k]["uid"] + == self.parent.SelectedItemData + ): # looking for the selected item, this is a request + selectedIndex = self.model.indexFromItem(col_item) + else: # this is an empty spot, no sample + position_s = str(j + 1) + item = QtGui.QStandardItem( + QtGui.QIcon( + ":/trolltech/styles/commonstyle/images/file-16.png" + ), + position_s, + ) + item.setData("", 32) + parentItem.appendRow(item) + self.setModel(self.model) + if selectedSampleIndex != None and collectionRunning == False: + self.setCurrentIndex(selectedSampleIndex) + if mountedIndex != None: + self.model.itemFromIndex(mountedIndex).setForeground( + QtGui.QColor("red") + ) + font = QtGui.QFont() + font.setUnderline(True) + font.setItalic(True) + font.setOverline(True) + self.model.itemFromIndex(mountedIndex).setFont(font) + self.parent.row_clicked(selectedSampleIndex) + elif selectedSampleIndex == None and collectionRunning == False: + if mountedIndex != None: + self.setCurrentIndex(mountedIndex) + self.model.itemFromIndex(mountedIndex).setForeground( + QtGui.QColor("red") + ) + font = QtGui.QFont() + font.setUnderline(True) + font.setItalic(True) + font.setOverline(True) + self.model.itemFromIndex(mountedIndex).setFont(font) + self.parent.row_clicked(mountedIndex) + else: + pass + if selectedIndex != None and collectionRunning == False: + self.setCurrentIndex(selectedIndex) + self.parent.row_clicked(selectedIndex) + if collectionRunning == True: + if mountedIndex != None: + self.setCurrentIndex(mountedIndex) + if self.isExpanded: + self.expandAll() + else: + self.collapseAll() + self.scrollTo(self.currentIndex(), QtWidgets.QAbstractItemView.PositionAtCenter) + + def refreshTreePriorityView( + self, + ): # "item" is a sample, "col_items" are requests which are children of samples. + collectionRunning = False + selectedIndex = None + mountedIndex = None + selectedSampleIndex = None + self.model.clear() + self.orderedRequests = db_lib.getOrderedRequestList(daq_utils.beamline) + dewarContents = db_lib.getContainerByName( + daq_utils.primaryDewarName, daq_utils.beamline + )["content"] + maxPucks = len(dewarContents) + requestedSampleList = [] + mountedPin = self.parent.mountedPin_pv.get() + for i in range( + len(self.orderedRequests) + ): # I need a list of samples for parent nodes + if self.orderedRequests[i]["sample"] not in requestedSampleList: + requestedSampleList.append(self.orderedRequests[i]["sample"]) + for i in range(len(requestedSampleList)): + sample = db_lib.getSampleByID(requestedSampleList[i]) + owner = sample["owner"] + parentItem = self.model.invisibleRootItem() + nodeString = str(db_lib.getSampleNamebyID(requestedSampleList[i])) + item = QtGui.QStandardItem( + QtGui.QIcon(":/trolltech/styles/commonstyle/images/file-16.png"), + nodeString, + ) + item.setData(requestedSampleList[i], 32) + item.setData("sample", 33) + if requestedSampleList[i] == mountedPin: + item.setForeground(QtGui.QColor("red")) + font = QtGui.QFont() + font.setItalic(True) + font.setOverline(True) + font.setUnderline(True) + item.setFont(font) + parentItem.appendRow(item) + if requestedSampleList[i] == mountedPin: + mountedIndex = self.model.indexFromItem(item) + if ( + requestedSampleList[i] == self.parent.selectedSampleID + ): # looking for the selected item + selectedSampleIndex = self.model.indexFromItem(item) + parentItem = item + for k in range(len(self.orderedRequests)): + if self.orderedRequests[k]["sample"] == requestedSampleList[i]: + col_item = QtGui.QStandardItem( + QtGui.QIcon( + ":/trolltech/styles/commonstyle/images/file-16.png" + ), + self.orderedRequests[k]["request_obj"]["file_prefix"] + + "_" + + self.orderedRequests[k]["request_obj"]["protocol"], + ) + col_item.setData(self.orderedRequests[k]["uid"], 32) + col_item.setData("request", 33) + col_item.setFlags( + Qt.ItemIsUserCheckable + | Qt.ItemIsEnabled + | Qt.ItemIsEditable + | Qt.ItemIsSelectable + ) + if self.orderedRequests[k]["priority"] == 99999: + col_item.setCheckState(Qt.Checked) + col_item.setBackground(QtGui.QColor("green")) + collectionRunning = True + self.parent.refreshCollectionParams( + self.orderedRequests[k], validate_hdf5=False + ) + + elif self.orderedRequests[k]["priority"] > 0: + col_item.setCheckState(Qt.Checked) + col_item.setBackground(QtGui.QColor("white")) + elif self.orderedRequests[k]["priority"] < 0: + col_item.setCheckable(False) + col_item.setBackground(QtGui.QColor("cyan")) + else: + col_item.setCheckState(Qt.Unchecked) + col_item.setBackground(QtGui.QColor("white")) + item.appendRow(col_item) + if ( + self.orderedRequests[k]["uid"] == self.parent.SelectedItemData + ): # looking for the selected item + selectedIndex = self.model.indexFromItem(col_item) + self.setModel(self.model) + if selectedSampleIndex != None and collectionRunning == False: + self.setCurrentIndex(selectedSampleIndex) + self.parent.row_clicked(selectedSampleIndex) + elif selectedSampleIndex == None and collectionRunning == False: + if mountedIndex != None: + self.setCurrentIndex(mountedIndex) + self.parent.row_clicked(mountedIndex) + else: + pass + + if selectedIndex != None and collectionRunning == False: + self.setCurrentIndex(selectedIndex) + self.parent.row_clicked(selectedIndex) + self.scrollTo(self.currentIndex(), QtWidgets.QAbstractItemView.PositionAtCenter) + self.expandAll() + + def queueSelectedSample(self, item): + reqID = str(item.data(32)) + checkedSampleRequest = db_lib.getRequestByID(reqID) # line not needed??? + if item.checkState() == Qt.Checked: + db_lib.updatePriority(reqID, 5000) + else: + db_lib.updatePriority(reqID, 0) + item.setBackground(QtGui.QColor("white")) + self.parent.treeChanged_pv.put( + self.parent.processID + ) # the idea is touch the pv, but have this gui instance not refresh + + def queueAllSelectedCB(self): + selmod = self.selectionModel() + selection = selmod.selection() + indexes = selection.indexes() + for i in range(len(indexes)): + item = self.model.itemFromIndex(indexes[i]) + itemData = str(item.data(32)) + itemDataType = str(item.data(33)) + if (itemDataType == "request") and item.isCheckable(): + selectedSampleRequest = db_lib.getRequestByID(itemData) + db_lib.updatePriority(itemData, 5000) + self.parent.treeChanged_pv.put(1) + + def deQueueAllSelectedCB(self): + selmod = self.selectionModel() + selection = selmod.selection() + indexes = selection.indexes() + for i in range(len(indexes)): + item = self.model.itemFromIndex(indexes[i]) + itemData = str(item.data(32)) + itemDataType = str(item.data(33)) + if (itemDataType == "request") and item.isCheckable(): + selectedSampleRequest = db_lib.getRequestByID(itemData) + db_lib.updatePriority(itemData, 0) + self.parent.treeChanged_pv.put(1) + + def confirmDelete(self, numReq): + if numReq: + quit_msg = f"Are you sure you want to delete {numReq} requests?" + else: + quit_msg = "Are you sure you want to delete all requests?" + self.parent.timerSample.stop() + reply = QtWidgets.QMessageBox.question( + self, + "Message", + quit_msg, + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No, + ) + self.parent.timerSample.start(SAMPLE_TIMER_DELAY) + if reply == QtWidgets.QMessageBox.Yes: + return 1 + else: + return 0 + + def deleteSelectedCB(self, deleteAll): + if deleteAll: + if not self.confirmDelete(0): + return + self.selectAll() + selmod = self.selectionModel() + selection = selmod.selection() + indexes = selection.indexes() + if len(indexes) > 1: + if not self.confirmDelete(len(indexes)): + return + progressInc = 100.0 / float(len(indexes)) + self.parent.progressDialog.setWindowTitle("Deleting Requests") + self.parent.progressDialog.show() + for i in range(len(indexes)): + self.parent.progressDialog.setValue(int((i + 1) * progressInc)) + item = self.model.itemFromIndex(indexes[i]) + itemData = str(item.data(32)) + itemDataType = str(item.data(33)) + if itemDataType == "request": + selectedSampleRequest = db_lib.getRequestByID(itemData) + self.selectedSampleID = selectedSampleRequest["sample"] + db_lib.deleteRequest(selectedSampleRequest["uid"]) + if selectedSampleRequest["request_obj"]["protocol"] in ( + "raster", + "stepRaster", + "multiCol", + ): + for i in range(len(self.parent.rasterList)): + if self.parent.rasterList[i] != None: + if ( + self.parent.rasterList[i]["uid"] + == selectedSampleRequest["uid"] + ): + self.parent.scene.removeItem( + self.parent.rasterList[i]["graphicsItem"] + ) + self.parent.rasterList[i] = None + if ( + selectedSampleRequest["request_obj"]["protocol"] == "vector" + or selectedSampleRequest["request_obj"]["protocol"] == "stepVector" + ): + self.parent.clearVectorCB() + self.parent.progressDialog.close() + self.parent.treeChanged_pv.put(1) + + def expandAllCB(self): + self.expandAll() + + def collapseAllCB(self): + self.collapseAll() + + def getSelectedSample(self): + selectedSampleID = None + if self.selectedIndexes(): + index = self.selectedIndexes()[0] + item = self.model.itemFromIndex(index) + if str(item.data(33)) == "sample": + selectedSampleID = str(item.data(32)) + elif str(item.data(33)) == "request": + selectedSampleRequest = db_lib.getRequestByID(item.data(32)) + selectedSampleID = selectedSampleRequest["sample"] + return selectedSampleID diff --git a/gui/dialog/__init__.py b/gui/dialog/__init__.py new file mode 100644 index 00000000..4a490c57 --- /dev/null +++ b/gui/dialog/__init__.py @@ -0,0 +1,7 @@ +from .snap_comment import SnapCommentDialog +from .raster_explore import RasterExploreDialog +from .staff_screen import StaffScreenDialog +from .user_screen import UserScreenDialog +from .puck_dialog import PuckDialog +from .dewar import DewarDialog +from .screen_defaults import ScreenDefaultsDialog diff --git a/gui/dialog/dewar.py b/gui/dialog/dewar.py new file mode 100644 index 00000000..b3ae55cd --- /dev/null +++ b/gui/dialog/dewar.py @@ -0,0 +1,92 @@ +import functools +import logging +import typing + +from qtpy import QtWidgets + +import daq_utils +import db_lib +from config_params import DEWAR_SECTORS, PUCKS_PER_DEWAR_SECTOR + +if typing.TYPE_CHECKING: + from lsdcGui import ControlMain + +logger = logging.getLogger() + + +class DewarDialog(QtWidgets.QDialog): + def __init__(self, parent: "ControlMain", action="add"): + super(DewarDialog, self).__init__(parent) + self.pucksPerDewarSector = PUCKS_PER_DEWAR_SECTOR[daq_utils.beamline] + self.dewarSectors = DEWAR_SECTORS[daq_utils.beamline] + self.action = action + self.parent = parent + + self.initData() + self.initUI() + + def initData(self): + dewarObj = db_lib.getPrimaryDewar(daq_utils.beamline) + puckLocs = dewarObj["content"] + self.data = [] + self.dewarPos = None + for i in range(len(puckLocs)): + if puckLocs[i] != "": + owner = db_lib.getContainerByID(puckLocs[i])["owner"] + self.data.append(db_lib.getContainerNameByID(puckLocs[i])) + else: + self.data.append("Empty") + logger.info(self.data) + + def initUI(self): + layout = QtWidgets.QVBoxLayout() + headerLabelLayout = QtWidgets.QHBoxLayout() + aLabel = QtWidgets.QLabel("A") + aLabel.setFixedWidth(15) + headerLabelLayout.addWidget(aLabel) + bLabel = QtWidgets.QLabel("B") + bLabel.setFixedWidth(10) + headerLabelLayout.addWidget(bLabel) + cLabel = QtWidgets.QLabel("C") + cLabel.setFixedWidth(10) + headerLabelLayout.addWidget(cLabel) + layout.addLayout(headerLabelLayout) + self.allButtonList = [None] * (self.dewarSectors * self.pucksPerDewarSector) + for i in range(0, self.dewarSectors): + rowLayout = QtWidgets.QHBoxLayout() + numLabel = QtWidgets.QLabel(str(i + 1)) + rowLayout.addWidget(numLabel) + for j in range(0, self.pucksPerDewarSector): + dataIndex = (i * self.pucksPerDewarSector) + j + self.allButtonList[dataIndex] = QtWidgets.QPushButton( + (str(self.data[dataIndex])) + ) + self.allButtonList[dataIndex].clicked.connect( + functools.partial(self.on_button, str(dataIndex)) + ) + rowLayout.addWidget(self.allButtonList[dataIndex]) + layout.addLayout(rowLayout) + cancelButton = QtWidgets.QPushButton("Done") + cancelButton.clicked.connect(self.containerCancelCB) + layout.addWidget(cancelButton) + self.setLayout(layout) + + def on_button(self, n): + if self.action == "remove": + self.dewarPos = n + db_lib.removePuckFromDewar(daq_utils.beamline, int(n)) + self.allButtonList[int(n)].setText("Empty") + self.parent.treeChanged_pv.put(1) + else: + self.dewarPos = n + self.accept() + + def containerCancelCB(self): + self.dewarPos = 0 + self.reject() + + @staticmethod + def getDewarPos(parent=None, action="add"): + dialog = DewarDialog(parent, action) + result = dialog.exec_() + return (dialog.dewarPos, result == QtWidgets.QDialog.Accepted) diff --git a/gui/dialog/puck_dialog.py b/gui/dialog/puck_dialog.py new file mode 100644 index 00000000..5d257f7b --- /dev/null +++ b/gui/dialog/puck_dialog.py @@ -0,0 +1,93 @@ +import logging +import typing + +from qtpy import QtCore, QtGui, QtWidgets +from qtpy.QtCore import Qt + +import daq_utils +import db_lib + +if typing.TYPE_CHECKING: + from lsdcGui import ControlMain + +logger = logging.getLogger() + + +class PuckDialog(QtWidgets.QDialog): + def __init__(self, parent: "ControlMain"): + super(PuckDialog, self).__init__(parent) + self.initData() + self.initUI() + + def initData(self): + puckListUnsorted = db_lib.getAllPucks(daq_utils.owner) + puckList = sorted(puckListUnsorted, key=lambda i: i["name"], reverse=False) + dewarObj = db_lib.getPrimaryDewar(daq_utils.beamline) + pucksInDewar = set(dewarObj["content"]) + self.model = QtGui.QStandardItemModel(self) + self.proxyModel = QtCore.QSortFilterProxyModel(self) + labels = ["Name"] + self.model.setHorizontalHeaderLabels(labels) + self.puckName = None + for puck in puckList: + if puck["uid"] not in pucksInDewar: + item = QtGui.QStandardItem(puck["name"]) + # Adding meta data to the puck. Each piece of meta data is identified using + # an int value, in this case is Qt.UserRole for puck modified time. This metadata is used + # to sort pucks + item.setData(puck.get("modified_time", 0), Qt.UserRole) + self.model.appendRow(item) + + def initUI(self): + self.tv = QtWidgets.QListView(self) + + self.tv.doubleClicked[QtCore.QModelIndex].connect(self.containerOKCB) + behavior = QtWidgets.QAbstractItemView.SelectRows + self.tv.setSelectionBehavior(behavior) + self.proxyModel.setSourceModel(self.model) + self.proxyModel.setSortRole(Qt.UserRole) + self.proxyModel.sort(0, order=Qt.DescendingOrder) + self.tv.setModel(self.proxyModel) + self.label = QtWidgets.QLabel(self) + self.buttons = QtWidgets.QDialogButtonBox( + QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, + Qt.Horizontal, + self, + ) + self.buttons.buttons()[0].clicked.connect(self.containerOKCB) + self.buttons.buttons()[1].clicked.connect(self.containerCancelCB) + self.searchBox = QtWidgets.QLineEdit(self) + self.searchBox.setPlaceholderText("Filter pucks...") + self.searchBox.textChanged.connect(self.filterPucks) + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.searchBox) + layout.addWidget(self.tv) + layout.addWidget(self.label) + layout.addWidget(self.buttons) + self.setLayout(layout) + + def filterPucks(self, a0: str): + self.proxyModel.setFilterFixedString(a0) + + def containerOKCB(self): + indexes = self.tv.selectedIndexes() + if indexes: + text = indexes[0].data() + self.label.setText(text) + self.puckName = text + self.accept() + else: + text = "" + self.puckName = text + self.reject() + + def containerCancelCB(self): + text = "" + self.reject() + self.puckName = text + + @staticmethod + def getPuckName(parent=None): + dialog = PuckDialog(parent) + result = dialog.exec_() + return (dialog.puckName, result == QtWidgets.QDialog.Accepted) diff --git a/gui/dialog/raster_explore.py b/gui/dialog/raster_explore.py new file mode 100644 index 00000000..e86f79e0 --- /dev/null +++ b/gui/dialog/raster_explore.py @@ -0,0 +1,53 @@ +from qtpy import QtWidgets +from qtpy.QtCore import Qt + + +class RasterExploreDialog(QtWidgets.QDialog): + def __init__(self): + QtWidgets.QDialog.__init__(self) + self.setModal(False) + self.setWindowTitle("Raster Explore") + vBoxParams1 = QtWidgets.QVBoxLayout() + hBoxParams1 = QtWidgets.QHBoxLayout() + hBoxParams2 = QtWidgets.QHBoxLayout() + hBoxParams3 = QtWidgets.QHBoxLayout() + spotCountLabel = QtWidgets.QLabel("Spot Count:") + spotCountLabel.setFixedWidth(120) + self.spotCount_ledit = QtWidgets.QLabel() + self.spotCount_ledit.setFixedWidth(60) + hBoxParams1.addWidget(spotCountLabel) + hBoxParams1.addWidget(self.spotCount_ledit) + intensityLabel = QtWidgets.QLabel("Total Intensity:") + intensityLabel.setFixedWidth(120) + self.intensity_ledit = QtWidgets.QLabel() + self.intensity_ledit.setFixedWidth(60) + hBoxParams2.addWidget(intensityLabel) + hBoxParams2.addWidget(self.intensity_ledit) + resoLabel = QtWidgets.QLabel("Resolution:") + resoLabel.setFixedWidth(120) + self.reso_ledit = QtWidgets.QLabel() + self.reso_ledit.setFixedWidth(60) + hBoxParams3.addWidget(resoLabel) + hBoxParams3.addWidget(self.reso_ledit) + + self.buttons = QtWidgets.QDialogButtonBox( + QtWidgets.QDialogButtonBox.Cancel, Qt.Horizontal, self + ) + self.buttons.buttons()[0].clicked.connect(self.rasterExploreCancelCB) + vBoxParams1.addLayout(hBoxParams1) + vBoxParams1.addLayout(hBoxParams2) + vBoxParams1.addLayout(hBoxParams3) + vBoxParams1.addWidget(self.buttons) + self.setLayout(vBoxParams1) + + def setSpotCount(self, val): + self.spotCount_ledit.setText(str(val)) + + def setTotalIntensity(self, val): + self.intensity_ledit.setText(str(val)) + + def setResolution(self, val): + self.reso_ledit.setText(str(val)) + + def rasterExploreCancelCB(self): + self.done(QtWidgets.QDialog.Rejected) diff --git a/gui/dialog/screen_defaults.py b/gui/dialog/screen_defaults.py new file mode 100644 index 00000000..a5f548c8 --- /dev/null +++ b/gui/dialog/screen_defaults.py @@ -0,0 +1,274 @@ +import logging +import typing + +from qtpy import QtCore, QtGui, QtWidgets + +import db_lib +from config_params import ( + RASTER_DOZOR_SPOT_LEVEL, + RASTER_TUNE_HIGH_RES, + RASTER_TUNE_ICE_RING_FLAG, + RASTER_TUNE_ICE_RING_WIDTH, + RASTER_TUNE_LOW_RES, + RASTER_TUNE_RESO_FLAG, + VALID_EXP_TIMES, +) +from daq_utils import beamline, getBlConfig, setBlConfig + +if typing.TYPE_CHECKING: + from lsdcGui import ControlMain + +logger = logging.getLogger() + + +class ScreenDefaultsDialog(QtWidgets.QDialog): + def __init__(self, parent: "ControlMain"): + QtWidgets.QDialog.__init__(self, parent) + self.parent = parent + self.setModal(False) + self.setWindowTitle("Raster Params") + + vBoxColParams1 = QtWidgets.QVBoxLayout() + + collectionGB = QtWidgets.QGroupBox() + collectionGB.setTitle("Collection parameters") + + hBoxColParams2 = QtWidgets.QHBoxLayout() + colRangeLabel = QtWidgets.QLabel("Oscillation Width:") + colRangeLabel.setAlignment(QtCore.Qt.AlignCenter) + self.osc_range_ledit = ( + QtWidgets.QLineEdit() + ) # note, this is for rastering! same name used for data collections + self.setGuiValues({"osc_range": getBlConfig("rasterDefaultWidth")}) + self.osc_range_ledit.returnPressed.connect(self.screenDefaultsOKCB) + colExptimeLabel = QtWidgets.QLabel("ExposureTime:") + colExptimeLabel.setAlignment(QtCore.Qt.AlignCenter) + self.exp_time_ledit = QtWidgets.QLineEdit() + self.setGuiValues({"exp_time": getBlConfig("rasterDefaultTime")}) + self.exp_time_ledit.returnPressed.connect(self.screenDefaultsOKCB) + self.exp_time_ledit.setValidator( + QtGui.QDoubleValidator( + VALID_EXP_TIMES[beamline]["min"], + VALID_EXP_TIMES[beamline]["max"], + VALID_EXP_TIMES[beamline]["digits"], + ) + ) + self.exp_time_ledit.textChanged.connect(self.checkEntryState) + + colTransLabel = QtWidgets.QLabel("Transmission (0.0-1.0):") + colTransLabel.setAlignment(QtCore.Qt.AlignCenter) + self.trans_ledit = QtWidgets.QLineEdit() + self.setGuiValues({"transmission": getBlConfig("rasterDefaultTrans")}) + self.trans_ledit.returnPressed.connect(self.screenDefaultsOKCB) + hBoxColParams2.addWidget(colRangeLabel) + hBoxColParams2.addWidget(self.osc_range_ledit) + hBoxColParams2.addWidget(colExptimeLabel) + hBoxColParams2.addWidget(self.exp_time_ledit) + hBoxColParams2.addWidget(colTransLabel) + hBoxColParams2.addWidget(self.trans_ledit) + collectionGB.setLayout(hBoxColParams2) + + dozorGB = QtWidgets.QGroupBox() + dozorGB.setTitle("Dozor Parameter") + hBoxColParams2a = QtWidgets.QHBoxLayout() + dozorSpotLevelLabel = QtWidgets.QLabel( + "Dozor Spot Level\n(Applies immediately)" + ) + self.dozorSpotLevel = QtWidgets.QComboBox() + self.dozorSpotLevel.addItems(["5", "6", "7", "8"]) + self.dozorSpotLevel.currentIndexChanged.connect(self.dozorSpotLevelChangedCB) + hBoxColParams2a.addWidget(dozorSpotLevelLabel) + hBoxColParams2a.addWidget(self.dozorSpotLevel) + dozorGB.setLayout(hBoxColParams2a) + + dialsGB = QtWidgets.QGroupBox() + dialsGB.setTitle("Dials Parameters") + vBoxDialsParams = QtWidgets.QVBoxLayout() + hBoxColParams2b = QtWidgets.QHBoxLayout() + colMinSpotLabel = QtWidgets.QLabel("Min Spot Size:") + colMinSpotLabel.setAlignment(QtCore.Qt.AlignCenter) + self.minSpot_ledit = QtWidgets.QLineEdit() + self.minSpot_ledit.setText(str(getBlConfig("rasterDefaultMinSpotSize"))) + self.minSpot_ledit.returnPressed.connect(self.screenDefaultsOKCB) + hBoxColParams2b.addWidget(colMinSpotLabel) + hBoxColParams2b.addWidget(self.minSpot_ledit) + + self.hBoxRasterLayout2 = QtWidgets.QHBoxLayout() + rasterTuneLabel = QtWidgets.QLabel("Raster\nTuning") + self.rasterResoCheckBox = QtWidgets.QCheckBox("Constrain Resolution") + self.rasterResoCheckBox.stateChanged.connect(self.rasterResoCheckCB) + rasterLowResLabel = QtWidgets.QLabel("LowRes:") + self.rasterLowRes = QtWidgets.QLineEdit() + self.rasterLowRes.setText(str(getBlConfig(RASTER_TUNE_LOW_RES))) + self.rasterLowRes.returnPressed.connect(self.screenDefaultsOKCB) + rasterHighResLabel = QtWidgets.QLabel("HighRes:") + self.rasterHighRes = QtWidgets.QLineEdit() + self.rasterHighRes.setText(str(getBlConfig(RASTER_TUNE_HIGH_RES))) + self.rasterHighRes.returnPressed.connect(self.screenDefaultsOKCB) + if getBlConfig(RASTER_TUNE_RESO_FLAG) == 1: + resoFlag = True + else: + resoFlag = False + self.rasterHighRes.setEnabled(False) + self.rasterLowRes.setEnabled(False) + self.rasterResoCheckBox.setChecked(resoFlag) + self.rasterIceRingCheckBox = QtWidgets.QCheckBox("Ice Ring") + self.rasterIceRingCheckBox.setChecked(False) + self.rasterIceRingCheckBox.stateChanged.connect(self.rasterIceRingCheckCB) + self.rasterIceRingWidth = QtWidgets.QLineEdit() + self.rasterIceRingWidth.setText(str(getBlConfig(RASTER_TUNE_ICE_RING_WIDTH))) + self.rasterIceRingWidth.returnPressed.connect(self.screenDefaultsOKCB) + self.rasterIceRingWidth.setEnabled(False) + if getBlConfig(RASTER_TUNE_ICE_RING_FLAG) == 1: + iceRingFlag = True + else: + iceRingFlag = False + self.rasterIceRingCheckBox.setChecked(iceRingFlag) + self.hBoxRasterLayout2.addWidget(self.rasterResoCheckBox) + self.hBoxRasterLayout2.addWidget(rasterLowResLabel) + self.hBoxRasterLayout2.addWidget(self.rasterLowRes) + self.hBoxRasterLayout2.addWidget(rasterHighResLabel) + self.hBoxRasterLayout2.addWidget(self.rasterHighRes) + self.hBoxRasterLayout2.addWidget(self.rasterIceRingCheckBox) + self.hBoxRasterLayout2.addWidget(self.rasterIceRingWidth) + + self.hBoxRasterLayout3 = QtWidgets.QHBoxLayout() + self.rasterThreshCheckBox = QtWidgets.QCheckBox("Tune Threshold") + if getBlConfig("rasterThreshFlag") == 1: + threshFlag = True + else: + threshFlag = False + self.rasterThreshCheckBox.setChecked(threshFlag) + self.rasterThreshCheckBox.stateChanged.connect(self.rasterThreshCheckCB) + + rasterThreshKernSizeLabel = QtWidgets.QLabel("KernelSize") + self.rasterThreshKernSize = QtWidgets.QLineEdit() + self.rasterThreshKernSize.setText(str(getBlConfig("rasterThreshKernSize"))) + self.rasterThreshKernSize.returnPressed.connect(self.screenDefaultsOKCB) + rasterThreshSigBckLabel = QtWidgets.QLabel("SigmaBkrnd") + self.rasterThreshSigBckrnd = QtWidgets.QLineEdit() + self.rasterThreshSigBckrnd.setText(str(getBlConfig("rasterThreshSigBckrnd"))) + self.rasterThreshSigBckrnd.returnPressed.connect(self.screenDefaultsOKCB) + rasterThreshSigStrongLabel = QtWidgets.QLabel("SigmaStrong") + self.rasterThreshSigStrong = QtWidgets.QLineEdit() + self.rasterThreshSigStrong.setText(str(getBlConfig("rasterThreshSigStrong"))) + self.rasterThreshSigStrong.returnPressed.connect(self.screenDefaultsOKCB) + self.rasterThreshKernSize.setEnabled(threshFlag) + self.rasterThreshSigBckrnd.setEnabled(threshFlag) + self.rasterThreshSigStrong.setEnabled(threshFlag) + self.hBoxRasterLayout3.addWidget(self.rasterThreshCheckBox) + self.hBoxRasterLayout3.addWidget(rasterThreshKernSizeLabel) + self.hBoxRasterLayout3.addWidget(self.rasterThreshKernSize) + self.hBoxRasterLayout3.addWidget(rasterThreshSigBckLabel) + self.hBoxRasterLayout3.addWidget(self.rasterThreshSigBckrnd) + self.hBoxRasterLayout3.addWidget(rasterThreshSigStrongLabel) + self.hBoxRasterLayout3.addWidget(self.rasterThreshSigStrong) + + vBoxDialsParams.addLayout(hBoxColParams2b) + vBoxDialsParams.addLayout(self.hBoxRasterLayout2) + vBoxDialsParams.addLayout(self.hBoxRasterLayout3) + dialsGB.setLayout(vBoxDialsParams) + + reprocessRasterButton = QtWidgets.QPushButton("ReProcessRaster") + reprocessRasterButton.clicked.connect(self.reprocessRasterRequestCB) + self.buttons = QtWidgets.QDialogButtonBox( + QtWidgets.QDialogButtonBox.Apply | QtWidgets.QDialogButtonBox.Cancel, + QtCore.Qt.Horizontal, + self, + ) + self.buttons.buttons()[1].clicked.connect(self.screenDefaultsOKCB) + self.buttons.buttons()[0].clicked.connect(self.screenDefaultsCancelCB) + vBoxColParams1.addWidget(collectionGB) + vBoxColParams1.addWidget(dozorGB) + vBoxColParams1.addWidget(dialsGB) + # vBoxColParams1.addWidget(reprocessRasterButton) + vBoxColParams1.addWidget(self.buttons) + self.setLayout(vBoxColParams1) + + def setGuiValues(self, values): + for item, value in values.items(): + logger.info("resetting %s to %s" % (item, value)) + if item == "osc_range": + self.osc_range_ledit.setText("%.3f" % float(value)) + elif item == "exp_time": + self.exp_time_ledit.setText("%.3f" % float(value)) + elif item == "transmission": + self.trans_ledit.setText("%.3f" % float(value)) + else: + logger.error("setGuiValues unknown item: %s value: %s" % (item, value)) + + def reprocessRasterRequestCB(self): + self.parent.eraseCB() + try: + reqID = self.parent.selectedSampleRequest["uid"] + self.parent.drawPolyRaster(db_lib.getRequestByID(reqID)) + self.parent.send_to_server('reprocessRaster("' + str(reqID) + '")') + except: + pass + + def screenDefaultsCancelCB(self): + self.done(QtWidgets.QDialog.Rejected) + + def dozorSpotLevelChangedCB(self, i): + setBlConfig(RASTER_DOZOR_SPOT_LEVEL, int(self.dozorSpotLevel.itemText(i))) + + def screenDefaultsOKCB(self): + setBlConfig("rasterDefaultWidth", float(self.osc_range_ledit.text())) + setBlConfig("rasterDefaultTime", float(self.exp_time_ledit.text())) + setBlConfig("rasterDefaultTrans", float(self.trans_ledit.text())) + setBlConfig("rasterDefaultMinSpotSize", float(self.minSpot_ledit.text())) + setBlConfig(RASTER_TUNE_LOW_RES, float(self.rasterLowRes.text())) + setBlConfig(RASTER_TUNE_HIGH_RES, float(self.rasterHighRes.text())) + setBlConfig(RASTER_TUNE_ICE_RING_WIDTH, float(self.rasterIceRingWidth.text())) + setBlConfig("rasterThreshKernSize", float(self.rasterThreshKernSize.text())) + setBlConfig("rasterThreshSigBckrnd", float(self.rasterThreshSigBckrnd.text())) + setBlConfig("rasterThreshSigStrong", float(self.rasterThreshSigStrong.text())) + if self.rasterIceRingCheckBox.isChecked(): + setBlConfig(RASTER_TUNE_ICE_RING_FLAG, 1) + else: + setBlConfig(RASTER_TUNE_ICE_RING_FLAG, 0) + if self.rasterResoCheckBox.isChecked(): + setBlConfig(RASTER_TUNE_RESO_FLAG, 1) + else: + setBlConfig(RASTER_TUNE_RESO_FLAG, 0) + + def rasterIceRingCheckCB(self, state): + if state == QtCore.Qt.Checked: + self.rasterIceRingWidth.setEnabled(True) + else: + self.rasterIceRingWidth.setEnabled(False) + + def rasterResoCheckCB(self, state): + if state == QtCore.Qt.Checked: + setBlConfig(RASTER_TUNE_RESO_FLAG, 1) + self.rasterLowRes.setEnabled(True) + self.rasterHighRes.setEnabled(True) + else: + setBlConfig(RASTER_TUNE_RESO_FLAG, 0) + self.rasterLowRes.setEnabled(False) + self.rasterHighRes.setEnabled(False) + + def rasterThreshCheckCB(self, state): + if state == QtCore.Qt.Checked: + setBlConfig("rasterThreshFlag", 1) + self.rasterThreshKernSize.setEnabled(True) + self.rasterThreshSigBckrnd.setEnabled(True) + self.rasterThreshSigStrong.setEnabled(True) + else: + setBlConfig("rasterThreshFlag", 0) + self.rasterThreshKernSize.setEnabled(False) + self.rasterThreshSigBckrnd.setEnabled(False) + self.rasterThreshSigStrong.setEnabled(False) + + # code below and its application from: https://snorfalorpagus.net/blog/2014/08/09/validating-user-input-in-pyqt4-using-qvalidator/ + def checkEntryState(self, *args, **kwargs): + sender = self.sender() + validator = sender.validator() + state = validator.validate(sender.text(), 0)[0] + if state == QtGui.QValidator.Intermediate: + color = "#fff79a" # yellow + elif state == QtGui.QValidator.Invalid: + color = "#f6989d" # red + else: + color = "#ffffff" # white + sender.setStyleSheet("QLineEdit { background-color: %s }" % color) diff --git a/gui/dialog/snap_comment.py b/gui/dialog/snap_comment.py new file mode 100644 index 00000000..c97a17b1 --- /dev/null +++ b/gui/dialog/snap_comment.py @@ -0,0 +1,40 @@ +from qtpy import QtWidgets + + +class SnapCommentDialog(QtWidgets.QDialog): + def __init__(self, parent=None): + QtWidgets.QDialog.__init__(self, parent) + self.setWindowTitle("Snapshot Comment") + self.setModal(False) + vBoxColParams1 = QtWidgets.QVBoxLayout() + hBoxColParams1 = QtWidgets.QHBoxLayout() + self.textEdit = QtWidgets.QPlainTextEdit() + vBoxColParams1.addWidget(self.textEdit) + self.ologCheckBox = QtWidgets.QCheckBox("Save to Olog") + self.ologCheckBox.setChecked(False) + vBoxColParams1.addWidget(self.ologCheckBox) + commentButton = QtWidgets.QPushButton("Add Comment") + commentButton.clicked.connect(self.commentCB) + cancelButton = QtWidgets.QPushButton("Cancel") + cancelButton.clicked.connect(self.cancelCB) + + hBoxColParams1.addWidget(commentButton) + hBoxColParams1.addWidget(cancelButton) + vBoxColParams1.addLayout(hBoxColParams1) + self.setLayout(vBoxColParams1) + + def cancelCB(self): + self.comment = "" + self.useOlog = False + self.reject() + + def commentCB(self): + self.comment = self.textEdit.toPlainText() + self.useOlog = self.ologCheckBox.isChecked() + self.accept() + + @staticmethod + def getComment(parent=None): + dialog = SnapCommentDialog(parent) + result = dialog.exec_() + return (dialog.comment, dialog.useOlog, result == QtWidgets.QDialog.Accepted) diff --git a/gui/dialog/staff_screen.py b/gui/dialog/staff_screen.py new file mode 100644 index 00000000..da40dc70 --- /dev/null +++ b/gui/dialog/staff_screen.py @@ -0,0 +1,365 @@ +import logging +import typing +import daq_utils + +from qtpy import QtCore, QtWidgets +from qtpy.QtWidgets import QCheckBox + +from config_params import BEAM_CHECK, TOP_VIEW_CHECK, UNMOUNT_COLD_CHECK +from daq_utils import getBlConfig, setBlConfig + +if typing.TYPE_CHECKING: + from lsdcGui import ControlMain + +logger = logging.getLogger() + + +class StaffScreenDialog(QtWidgets.QFrame): + def __init__(self, parent: "ControlMain", **kwargs): + show = kwargs.get("show", True) + self.parent = parent + QtWidgets.QFrame.__init__(self) + self.setWindowTitle("Staff Only") + self.spotNodeCount = 8 + self.fastDPNodeCount = 4 + self.cpuCount = 28 + vBoxColParams1 = QtWidgets.QVBoxLayout() + hBoxColParams0 = QtWidgets.QHBoxLayout() + hBoxColParams1 = QtWidgets.QHBoxLayout() + hBoxColParams2 = QtWidgets.QHBoxLayout() + hBoxColParams3 = QtWidgets.QHBoxLayout() + hBoxFastDP = QtWidgets.QHBoxLayout() + hBoxSpotfinder = QtWidgets.QHBoxLayout() + puckToDewarButton = QtWidgets.QPushButton("Puck to Dewar...") + puckToDewarButton.clicked.connect(self.parent.puckToDewarCB) + removePuckButton = QtWidgets.QPushButton("Remove Puck...") + removePuckButton.clicked.connect(self.parent.removePuckCB) + hBoxColParams0.addWidget(puckToDewarButton) + hBoxColParams0.addWidget(removePuckButton) + self.robotOnCheckBox = QCheckBox("Robot (On)") + if getBlConfig("robot_online") == 1: + self.robotOnCheckBox.setChecked(True) + else: + self.robotOnCheckBox.setChecked(False) + self.robotOnCheckBox.stateChanged.connect(self.robotOnCheckCB) + self.topViewCheckOnCheckBox = QCheckBox("TopViewCheck (On)") + if getBlConfig(TOP_VIEW_CHECK) == 1: + self.topViewCheckOnCheckBox.setChecked(True) + else: + self.topViewCheckOnCheckBox.setChecked(False) + self.topViewCheckOnCheckBox.stateChanged.connect(self.topViewOnCheckCB) + # BeamCheck check box + self.beamCheckOnCheckBox = QCheckBox("BeamCheck (On)") + if getBlConfig(BEAM_CHECK) == 1: + self.beamCheckOnCheckBox.setChecked(True) + else: + self.beamCheckOnCheckBox.setChecked(False) + self.beamCheckOnCheckBox.stateChanged.connect(self.beamCheckOnCheckCB) + + self.gripperUnmountColdCheckBox = QCheckBox("Unmount Cold") + self.gripperUnmountColdCheckBox.stateChanged.connect(self.unmountColdCheckCB) + if getBlConfig(UNMOUNT_COLD_CHECK) == 1: + self.gripperUnmountColdCheckBox.setEnabled(True) + self.gripperUnmountColdCheckBox.setChecked(True) + else: + self.gripperUnmountColdCheckBox.setEnabled(False) + self.gripperUnmountColdCheckBox.setChecked(False) + + self.queueCollectOnCheckBox = QCheckBox("Queue Collect") + hBoxColParams1.addWidget(self.queueCollectOnCheckBox) + if getBlConfig("queueCollect") == 1: + self.queueCollectOnCheckBox.setChecked(True) + self.gripperUnmountColdCheckBox.setEnabled(True) + self.parent.queue_collect_status_widget.setText("Queue Collect: ON") + else: + self.queueCollectOnCheckBox.setChecked(False) + self.gripperUnmountColdCheckBox.setEnabled(False) + self.parent.queue_collect_status_widget.setText("Queue Collect: OFF") + self.queueCollectOnCheckBox.stateChanged.connect(self.queueCollectOnCheckCB) + self.vertRasterOnCheckBox = QCheckBox("Vert. Raster") + hBoxColParams1.addWidget(self.vertRasterOnCheckBox) + if getBlConfig("vertRasterOn") == 1: + self.vertRasterOnCheckBox.setChecked(True) + else: + self.vertRasterOnCheckBox.setChecked(False) + self.vertRasterOnCheckBox.stateChanged.connect(self.vertRasterOnCheckCB) + self.procRasterOnCheckBox = QCheckBox("Process Raster") + hBoxColParams1.addWidget(self.procRasterOnCheckBox) + if getBlConfig("rasterProcessFlag") == 1: + self.procRasterOnCheckBox.setChecked(True) + else: + self.procRasterOnCheckBox.setChecked(False) + self.procRasterOnCheckBox.stateChanged.connect(self.procRasterOnCheckCB) + self.guiRemoteOnCheckBox = QCheckBox("GUI Remote") + hBoxColParams1.addWidget(self.guiRemoteOnCheckBox) + if getBlConfig("omegaMonitorPV") == "VAL": + self.guiRemoteOnCheckBox.setChecked(True) + else: + self.guiRemoteOnCheckBox.setChecked(False) + self.guiRemoteOnCheckBox.stateChanged.connect(self.guiRemoteOnCheckCB) + self.albulaDispCheckBox = QCheckBox("Display Data (Albula)") + self.albulaDispCheckBox.setChecked(True) + hBoxColParams1.addWidget(self.albulaDispCheckBox) + + self.enableMountCheckBox = QCheckBox("Enable Mount") + if getBlConfig("mountEnabled") == 1: + self.enableMountCheckBox.setChecked(True) + else: + self.enableMountCheckBox.setChecked(False) + self.enableMountCheckBox.stateChanged.connect(self.enableMountCheckCB) + self.unmountColdButton = QtWidgets.QPushButton("Unmount Cold") + self.unmountColdButton.clicked.connect(self.unmountColdCB) + self.openPort1Button = QtWidgets.QPushButton("Open Port 1") + self.openPort1Button.clicked.connect(self.openPort1CB) + self.closePortsButton = QtWidgets.QPushButton("Close Ports") + self.closePortsButton.clicked.connect(self.closePortsCB) + self.warmupButton = QtWidgets.QPushButton("Dry Gripper") + self.warmupButton.clicked.connect(self.parent.dryGripperCB) + self.enableTScreenButton = QtWidgets.QPushButton("Enable Dewar Tscreen") + self.enableTScreenButton.clicked.connect(self.parent.enableTScreenGripperCB) + self.parkButton = QtWidgets.QPushButton("Park Gripper") + self.parkButton.clicked.connect(self.parent.parkGripperCB) + self.homePinsButton = QtWidgets.QPushButton("Home Pins") + self.homePinsButton.clicked.connect(self.homePinsCB) + self.clearMountedSampleButton = QtWidgets.QPushButton("Clear Mounted Sample") + self.clearMountedSampleButton.clicked.connect(self.clearMountedSampleCB) + hBoxColParams2.addWidget(self.openPort1Button) + hBoxColParams2.addWidget(self.closePortsButton) + hBoxColParams2.addWidget(self.unmountColdButton) + hBoxColParams2.addWidget(self.warmupButton) + hBoxColParams2.addWidget(self.enableTScreenButton) + hBoxColParams2.addWidget(self.parkButton) + hBoxColParams2.addWidget(self.clearMountedSampleButton) + hBoxColParams1.addWidget(self.homePinsButton) + self.setFastDPNodesButton = QtWidgets.QPushButton("Set FastDP Nodes") + self.setFastDPNodesButton.clicked.connect(self.setFastDPNodesCB) + hBoxFastDP.addWidget(self.setFastDPNodesButton) + self.fastDPNodeEntryList = [] + nodeList = self.getFastDPNodeList() + for i in range(0, self.fastDPNodeCount): + self.fastDPNodeEntryList.append(QtWidgets.QLineEdit()) + self.fastDPNodeEntryList[i].setFixedWidth(30) + self.fastDPNodeEntryList[i].setText(str(nodeList[i])) + hBoxFastDP.addWidget(self.fastDPNodeEntryList[i]) + self.fastDPCheckBox = QCheckBox("FastDP") + self.fastDPCheckBox.setChecked(True) + hBoxFastDP.addWidget(self.fastDPCheckBox) + self.setBeamcenterButton = QtWidgets.QPushButton("Set Beamcenter") + self.setBeamcenterButton.clicked.connect(self.setBeamcenterCB) + hBoxFastDP.addWidget(self.setBeamcenterButton) + self.beamcenterX_ledit = QtWidgets.QLineEdit() + self.beamcenterX_ledit.setText(str(self.parent.beamCenterX_pv.get())) + self.beamcenterY_ledit = QtWidgets.QLineEdit() + self.beamcenterY_ledit.setText(str(self.parent.beamCenterY_pv.get())) + hBoxFastDP.addWidget(self.beamcenterX_ledit) + hBoxFastDP.addWidget(self.beamcenterY_ledit) + self.setSpotNodesButton = QtWidgets.QPushButton("Set Spotfinder Nodes") + self.setSpotNodesButton.clicked.connect(self.setSpotNodesCB) + self.lockGuiButton = QtWidgets.QPushButton("Lock") + self.lockGuiButton.clicked.connect(self.lockGuiCB) + self.unLockGuiButton = QtWidgets.QPushButton("unLock") + self.unLockGuiButton.clicked.connect(self.unLockGuiCB) + hBoxSpotfinder.addWidget(self.lockGuiButton) + hBoxSpotfinder.addWidget(self.unLockGuiButton) + hBoxSpotfinder.addWidget(self.setSpotNodesButton) + self.spotNodeEntryList = [] + nodeList = self.getSpotNodeList() + for i in range(0, self.spotNodeCount): + self.spotNodeEntryList.append(QtWidgets.QLineEdit()) + self.spotNodeEntryList[i].setFixedWidth(30) + self.spotNodeEntryList[i].setText(str(nodeList[i])) + hBoxSpotfinder.addWidget(self.spotNodeEntryList[i]) + robotGB = QtWidgets.QGroupBox() + robotGB.setTitle("Robot") + hBoxRobot1 = QtWidgets.QHBoxLayout() + vBoxRobot1 = QtWidgets.QVBoxLayout() + self.recoverRobotButton = QtWidgets.QPushButton("Recover Robot") + self.recoverRobotButton.clicked.connect(self.recoverRobotCB) + self.rebootEMBLButton = QtWidgets.QPushButton("Reboot EMBL") + self.rebootEMBLButton.clicked.connect(self.rebootEMBL_CB) + self.restartEMBLButton = QtWidgets.QPushButton("Start EMBL") + self.restartEMBLButton.clicked.connect(self.restartEMBL_CB) + self.openGripperButton = QtWidgets.QPushButton("Open Gripper") + self.openGripperButton.clicked.connect(self.openGripper_CB) + self.closeGripperButton = QtWidgets.QPushButton("Close Gripper") + self.closeGripperButton.clicked.connect(self.closeGripper_CB) + hBoxRobot1.addWidget(self.robotOnCheckBox) + hBoxRobot1.addWidget(self.beamCheckOnCheckBox) + hBoxRobot1.addWidget(self.gripperUnmountColdCheckBox) + hBoxRobot1.addWidget(self.topViewCheckOnCheckBox) + hBoxRobot1.addWidget(self.enableMountCheckBox) + hBoxRobot1.addWidget(self.recoverRobotButton) + hBoxRobot1.addWidget(self.rebootEMBLButton) + hBoxRobot1.addWidget(self.restartEMBLButton) + hBoxRobot1.addWidget(self.openGripperButton) + hBoxRobot1.addWidget(self.closeGripperButton) + vBoxRobot1.addLayout(hBoxRobot1) + vBoxRobot1.addLayout(hBoxColParams2) + robotGB.setLayout(vBoxRobot1) + self.buttons = QtWidgets.QDialogButtonBox( + QtWidgets.QDialogButtonBox.Ok, QtCore.Qt.Horizontal, self + ) + self.buttons.buttons()[0].clicked.connect(self.screenDefaultsOKCB) + vBoxColParams1.addLayout(hBoxColParams0) + vBoxColParams1.addLayout(hBoxColParams1) + vBoxColParams1.addLayout(hBoxFastDP) + vBoxColParams1.addLayout(hBoxSpotfinder) + vBoxColParams1.addWidget(robotGB) + vBoxColParams1.addWidget(self.buttons) + self.setLayout(vBoxColParams1) + if show: + self.show() + + def getSpotNodeList(self): + nodeList = [] + for i in range(0, self.spotNodeCount): + if daq_utils.beamline == 'nyx': + nodeList.append(int(getBlConfig("spotNode"+str(i+1)).split('epu')[1])) + else: + nodeList.append(int(getBlConfig("spotNode"+str(i+1)).split('cpu')[1])) + return nodeList + + def getFastDPNodeList(self): + nodeList = [] + for i in range(0, self.fastDPNodeCount): + nodeList.append(int(getBlConfig("fastDPNode" + str(i + 1)).split("cpu")[1])) + return nodeList + + def setFastDPNodesCB(self): + comm_s = "fastDPNodes(" + for i in range(0, self.fastDPNodeCount): + comm_s = comm_s + str(self.fastDPNodeEntryList[i].text()) + if i == self.fastDPNodeCount - 1: + comm_s = comm_s + ")" + else: + comm_s = comm_s + "," + logger.info(comm_s) + self.parent.send_to_server(comm_s) + + def lockGuiCB(self): + self.parent.send_to_server("lockControl") + + def unLockGuiCB(self): + self.parent.send_to_server("unlockControl") + + def setSpotNodesCB(self): + comm_s = "spotNodes(" + for i in range(0, self.spotNodeCount): + comm_s = comm_s + str(self.spotNodeEntryList[i].text()) + if i == self.spotNodeCount - 1: + comm_s = comm_s + ")" + else: + comm_s = comm_s + "," + logger.info(comm_s) + self.parent.send_to_server(comm_s) + + def unmountColdCB(self): + self.parent.send_to_server("unmountCold()") + + def openPort1CB(self): + self.parent.send_to_server("openPort(1)") + + def setBeamcenterCB(self): + self.parent.send_to_server( + "set_beamcenter (" + + str(self.beamcenterX_ledit.text()) + + "," + + str(self.beamcenterY_ledit.text()) + + ")" + ) + + def closePortsCB(self): + self.parent.send_to_server("closePorts()") + + def clearMountedSampleCB(self): + self.parent.send_to_server("clearMountedSample()") + + def recoverRobotCB(self): + self.parent.aux_send_to_server("recoverRobot()") + + def rebootEMBL_CB(self): + self.parent.aux_send_to_server("rebootEMBL()") + + def restartEMBL_CB(self): + self.parent.send_to_server("restartEMBL()") + + def openGripper_CB(self): + self.parent.send_to_server("openGripper()") + + def closeGripper_CB(self): + self.parent.send_to_server("closeGripper()") + + def homePinsCB(self): + self.parent.send_to_server("homePins()") + + def robotOnCheckCB(self, state): + if state == QtCore.Qt.Checked: + setBlConfig("robot_online", 1) + else: + setBlConfig("robot_online", 0) + + def beamCheckOnCheckCB(self, state): + if state == QtCore.Qt.Checked: + setBlConfig(BEAM_CHECK, 1) + logger.debug(f"{BEAM_CHECK} on") + else: + setBlConfig(BEAM_CHECK, 0) + logger.debug(f"{BEAM_CHECK} off") + + def unmountColdCheckCB(self, state): + if state == QtCore.Qt.Checked: + logger.info("unmountColdCheckCB On") + setBlConfig(UNMOUNT_COLD_CHECK, 1) + else: + logger.info("unmountColdCheckCB Off") + setBlConfig(UNMOUNT_COLD_CHECK, 0) + + def topViewOnCheckCB(self, state): + if state == QtCore.Qt.Checked: + setBlConfig(TOP_VIEW_CHECK, 1) + else: + setBlConfig(TOP_VIEW_CHECK, 0) + + def vertRasterOnCheckCB(self, state): + if state == QtCore.Qt.Checked: + setBlConfig("vertRasterOn", 1) + else: + setBlConfig("vertRasterOn", 0) + + def procRasterOnCheckCB(self, state): + if state == QtCore.Qt.Checked: + setBlConfig("rasterProcessFlag", 1) + else: + setBlConfig("rasterProcessFlag", 0) + + def guiRemoteOnCheckCB(self, state): + if state == QtCore.Qt.Checked: + setBlConfig("omegaMonitorPV", "VAL") + else: + setBlConfig("omegaMonitorPV", "RBV") + + def queueCollectOnCheckCB(self, state): + if state == QtCore.Qt.Checked: + setBlConfig("queueCollect", 1) + self.gripperUnmountColdCheckBox.setEnabled(True) + self.parent.queue_collect_status_widget.setText("Queue Collect: ON") + else: + setBlConfig("queueCollect", 0) + self.gripperUnmountColdCheckBox.setEnabled(False) + self.gripperUnmountColdCheckBox.setChecked(False) + self.parent.queue_collect_status_widget.setText("Queue Collect: OFF") + self.parent.row_clicked( + 0 + ) # This is so that appropriate boxes are filled when toggling queue collect + + def enableMountCheckCB(self, state): + if state == QtCore.Qt.Checked: + setBlConfig("mountEnabled", 1) + else: + setBlConfig("mountEnabled", 0) + + def screenDefaultsCancelCB(self): + self.hide() + + def screenDefaultsOKCB(self): + self.hide() diff --git a/gui/dialog/user_screen.py b/gui/dialog/user_screen.py new file mode 100644 index 00000000..e4c0ef20 --- /dev/null +++ b/gui/dialog/user_screen.py @@ -0,0 +1,256 @@ +import logging +import typing + +from qt_epics.QtEpicsPVLabel import QtEpicsPVLabel +from qtpy import QtCore, QtWidgets +from qtpy.QtWidgets import QCheckBox + +import daq_utils + +if typing.TYPE_CHECKING: + from lsdcGui import ControlMain + +logger = logging.getLogger() + + +class UserScreenDialog(QtWidgets.QFrame): + def __init__(self, parent: "ControlMain"): + self.parent = parent + QtWidgets.QFrame.__init__(self) + self.setWindowTitle("User Extras") + vBoxColParams1 = QtWidgets.QVBoxLayout() + hBoxColParams1 = QtWidgets.QHBoxLayout() + hBoxColParams2 = QtWidgets.QHBoxLayout() + hBoxColParams25 = QtWidgets.QHBoxLayout() + hBoxColParams3 = QtWidgets.QHBoxLayout() + govLabel = QtWidgets.QLabel("Set Governor State:") + self.SEbutton = QtWidgets.QPushButton("SE") + self.SEbutton.clicked.connect(self.SEgovCB) + self.SAbutton = QtWidgets.QPushButton("SA") + self.SAbutton.clicked.connect(self.SAgovCB) + self.DAbutton = QtWidgets.QPushButton("DA") + self.DAbutton.clicked.connect(self.DAgovCB) + self.BLbutton = QtWidgets.QPushButton("BL") + self.BLbutton.clicked.connect(self.BLgovCB) + hBoxColParams1.addWidget(govLabel) + hBoxColParams1.addWidget(self.SEbutton) + hBoxColParams1.addWidget(self.SAbutton) + hBoxColParams1.addWidget(self.DAbutton) + hBoxColParams1.addWidget(self.BLbutton) + govLabel2 = QtWidgets.QLabel("Current Governor State:") + self.governorMessage = QtEpicsPVLabel( + daq_utils.pvLookupDict["governorMessage"], + self, + 140, + highlight_on_change=False, + ) + hBoxColParams2.addWidget(govLabel2) + hBoxColParams2.addWidget(self.governorMessage.getEntry()) + + self.openShutterButton = QtWidgets.QPushButton("Open Photon Shutter") + self.openShutterButton.clicked.connect(self.parent.openPhotonShutterCB) + hBoxColParams25.addWidget(self.openShutterButton) + robotGB = QtWidgets.QGroupBox() + robotGB.setTitle("Robot") + + self.unmountColdButton = QtWidgets.QPushButton("Unmount Cold") + self.unmountColdButton.clicked.connect(self.unmountColdCB) + self.testRobotButton = QtWidgets.QPushButton("Test Robot") + self.testRobotButton.clicked.connect(self.testRobotCB) + self.recoverRobotButton = QtWidgets.QPushButton("Recover Robot") + self.recoverRobotButton.clicked.connect(self.recoverRobotCB) + self.dryGripperButton = QtWidgets.QPushButton("Dry Gripper") + self.dryGripperButton.clicked.connect(self.dryGripperCB) + + hBoxColParams3.addWidget(self.unmountColdButton) + hBoxColParams3.addWidget(self.testRobotButton) + hBoxColParams3.addWidget(self.recoverRobotButton) + hBoxColParams3.addWidget(self.dryGripperButton) + robotGB.setLayout(hBoxColParams3) + + zebraGB = QtWidgets.QGroupBox() + detGB = QtWidgets.QGroupBox() + zebraGB.setTitle("Zebra (Timing)") + detGB.setTitle("Eiger Detector") + hBoxDet1 = QtWidgets.QHBoxLayout() + hBoxDet2 = QtWidgets.QHBoxLayout() + vBoxDet1 = QtWidgets.QVBoxLayout() + self.stopDetButton = QtWidgets.QPushButton("Stop") + self.stopDetButton.clicked.connect(self.stopDetCB) + self.rebootDetIocButton = QtWidgets.QPushButton("Reboot Det IOC") + self.rebootDetIocButton.clicked.connect(self.rebootDetIocCB) + detStatLabel = QtWidgets.QLabel("Detector Status:") + self.detMessage_ledit = QtWidgets.QLabel() + hBoxDet1.addWidget(self.stopDetButton) + hBoxDet1.addWidget(self.rebootDetIocButton) + hBoxDet2.addWidget(detStatLabel) + hBoxDet2.addWidget(self.detMessage_ledit) + + beamGB = QtWidgets.QGroupBox() + beamGB.setTitle("Beam") + hBoxBeam1 = QtWidgets.QHBoxLayout() + hBoxBeam2 = QtWidgets.QHBoxLayout() + hBoxBeam3 = QtWidgets.QHBoxLayout() + vBoxBeam = QtWidgets.QVBoxLayout() + if daq_utils.beamline == "fmx": + slit1XLabel = QtWidgets.QLabel("Slit 1 X Gap:") + slit1XLabel.setAlignment(QtCore.Qt.AlignCenter) + slit1XRBLabel = QtWidgets.QLabel("Readback:") + self.slit1XRBVLabel = QtEpicsPVLabel( + daq_utils.motor_dict["slit1XGap"] + ".RBV", self, 70 + ) + slit1XSPLabel = QtWidgets.QLabel("SetPoint:") + self.slit1XMotor_ledit = QtWidgets.QLineEdit() + self.slit1XMotor_ledit.returnPressed.connect(self.setSlit1XCB) + self.slit1XMotor_ledit.setText(str(self.parent.slit1XGapSP_pv.get())) + + slit1YLabel = QtWidgets.QLabel("Slit 1 Y Gap:") + slit1YLabel.setAlignment(QtCore.Qt.AlignCenter) + slit1YRBLabel = QtWidgets.QLabel("Readback:") + self.slit1YRBVLabel = QtEpicsPVLabel( + daq_utils.motor_dict["slit1YGap"] + ".RBV", self, 70 + ) + slit1YSPLabel = QtWidgets.QLabel("SetPoint:") + self.slit1YMotor_ledit = QtWidgets.QLineEdit() + self.slit1YMotor_ledit.setText(str(self.parent.slit1YGapSP_pv.get())) + self.slit1YMotor_ledit.returnPressed.connect(self.setSlit1YCB) + + sampleFluxLabelDesc = QtWidgets.QLabel("Sample Flux:") + sampleFluxLabelDesc.setFixedWidth(80) + self.sampleFluxLabel = QtWidgets.QLabel() + self.sampleFluxLabel.setText("%E" % self.parent.sampleFluxPV.get()) + hBoxBeam3.addWidget(sampleFluxLabelDesc) + hBoxBeam3.addWidget(self.sampleFluxLabel) + + if daq_utils.beamline == "fmx": + hBoxBeam1.addWidget(slit1XLabel) + hBoxBeam1.addWidget(slit1XRBLabel) + hBoxBeam1.addWidget(self.slit1XRBVLabel.getEntry()) + hBoxBeam1.addWidget(slit1XSPLabel) + hBoxBeam1.addWidget(self.slit1XMotor_ledit) + hBoxBeam2.addWidget(slit1YLabel) + hBoxBeam2.addWidget(slit1YRBLabel) + hBoxBeam2.addWidget(self.slit1YRBVLabel.getEntry()) + hBoxBeam2.addWidget(slit1YSPLabel) + hBoxBeam2.addWidget(self.slit1YMotor_ledit) + vBoxBeam.addLayout(hBoxBeam1) + vBoxBeam.addLayout(hBoxBeam2) + vBoxBeam.addLayout(hBoxBeam3) + beamGB.setLayout(vBoxBeam) + + vBoxDet1.addLayout(hBoxDet1) + vBoxDet1.addLayout(hBoxDet2) + detGB.setLayout(vBoxDet1) + hBoxColParams4 = QtWidgets.QHBoxLayout() + vBoxZebraParams4 = QtWidgets.QVBoxLayout() + self.resetZebraButton = QtWidgets.QPushButton("Reset Zebra") + self.resetZebraButton.clicked.connect(self.resetZebraCB) + self.rebootZebraButton = QtWidgets.QPushButton("Reboot Zebra IOC") + self.rebootZebraButton.clicked.connect(self.rebootZebraIOC_CB) + hBoxColParams5 = QtWidgets.QHBoxLayout() + self.zebraArmCheckBox = QCheckBox("Arm") + self.zebraArmCheckBox.setEnabled(False) + self.zebraPulseCheckBox = QCheckBox("Pulse") + self.zebraPulseCheckBox.setEnabled(False) + self.zebraDownloadCheckBox = QCheckBox("Downloading") + self.zebraDownloadCheckBox.setEnabled(False) + self.zebraSentTriggerCheckBox = QCheckBox("Trigger Sent") + self.zebraSentTriggerCheckBox.setEnabled(False) + self.zebraReturnedTriggerCheckBox = QCheckBox("Trigger Returned") + self.zebraReturnedTriggerCheckBox.setEnabled(False) + hBoxColParams4.addWidget(self.resetZebraButton) + hBoxColParams4.addWidget(self.rebootZebraButton) + hBoxColParams5.addWidget(self.zebraArmCheckBox) + hBoxColParams5.addWidget(self.zebraPulseCheckBox) + hBoxColParams5.addWidget(self.zebraDownloadCheckBox) + hBoxColParams5.addWidget(self.zebraSentTriggerCheckBox) + hBoxColParams5.addWidget(self.zebraReturnedTriggerCheckBox) + vBoxZebraParams4.addLayout(hBoxColParams4) + vBoxZebraParams4.addLayout(hBoxColParams5) + zebraGB.setLayout(vBoxZebraParams4) + + self.buttons = QtWidgets.QDialogButtonBox( + QtWidgets.QDialogButtonBox.Ok, QtCore.Qt.Horizontal, self + ) + self.buttons.buttons()[0].clicked.connect(self.userScreenOKCB) + + if daq_utils.beamline == "nyx": + self.openShutterButton.setDisabled(True) + self.unmountColdButton.setDisabled(True) + self.testRobotButton.setDisabled(True) + self.recoverRobotButton.setDisabled(True) + self.dryGripperButton.setDisabled(True) + self.resetZebraButton.setDisabled(True) + self.rebootZebraButton.setDisabled(True) + self.stopDetButton.setDisabled(True) + self.rebootDetIocButton.setDisabled(True) + + vBoxColParams1.addLayout(hBoxColParams1) + vBoxColParams1.addLayout(hBoxColParams2) + vBoxColParams1.addLayout(hBoxColParams25) + vBoxColParams1.addWidget(robotGB) + vBoxColParams1.addWidget(zebraGB) + vBoxColParams1.addWidget(detGB) + vBoxColParams1.addWidget(beamGB) + + vBoxColParams1.addWidget(self.buttons) + self.setLayout(vBoxColParams1) + + def setSlit1XCB(self): + comm_s = "setSlit1X(" + str(self.slit1XMotor_ledit.text()) + ")" + self.parent.send_to_server(comm_s) + + def setSlit1YCB(self): + comm_s = "setSlit1Y(" + str(self.slit1YMotor_ledit.text()) + ")" + self.parent.send_to_server(comm_s) + + def unmountColdCB(self): + self.parent.send_to_server("unmountCold()") + + def testRobotCB(self): + self.parent.send_to_server("testRobot()") + + def recoverRobotCB(self): + self.parent.send_to_server("recoverRobot()") + + def dryGripperCB(self): + self.parent.send_to_server("dryGripper()") + + def stopDetCB(self): + logger.info("stopping detector") + self.parent.stopDet_pv.put(0) + + def rebootDetIocCB(self): + logger.info("rebooting detector IOC") + self.parent.rebootDetIOC_pv.put( + 1 + ) # no differences visible, but zebra IOC reboot works, this doesn't! + + def resetZebraCB(self): + logger.info("resetting zebra") + self.parent.resetZebra_pv.put(1) + + def rebootZebraIOC_CB(self): + logger.info("rebooting zebra IOC") + self.parent.rebootZebraIOC_pv.put(1) + + def SEgovCB(self): + self.parent.send_to_server("setGovRobot(gov_robot, 'SE')") + + def SAgovCB(self): + self.parent.send_to_server("setGovRobot(gov_robot, 'SA')") + + def DAgovCB(self): + self.parent.send_to_server("setGovRobot(gov_robot, 'DA')") + + def BLgovCB(self): + self.parent.send_to_server("setGovRobot(gov_robot, 'BL')") + + def userScreenOKCB(self): + self.hide() + + def screenDefaultsCancelCB(self): + self.done(QtWidgets.QDialog.Rejected) + + def screenDefaultsOKCB(self): + self.done(QtWidgets.QDialog.Accepted) diff --git a/gui/raster.py b/gui/raster.py new file mode 100644 index 00000000..49be3ff1 --- /dev/null +++ b/gui/raster.py @@ -0,0 +1,98 @@ +import logging +import typing + +from qtpy import QtCore, QtWidgets + +import albulaUtils + +if typing.TYPE_CHECKING: + from lsdcGui import ControlMain + +logger = logging.getLogger() + + +class RasterCell(QtWidgets.QGraphicsRectItem): + def __init__(self, x, y, w, h, topParent): + super(RasterCell, self).__init__(x, y, w, h, None) + self.topParent = topParent + + +def isInCell(position, item): + if item.contains(position): + return True + return False + + +class RasterGroup(QtWidgets.QGraphicsItemGroup): + def __init__(self, parent: "ControlMain"): + super(RasterGroup, self).__init__() + self.parent = parent + self.setAcceptHoverEvents(True) + self.currentSelectedCell = None + + def mousePressEvent(self, e): + for i in range(len(self.parent.rasterList)): + if self.parent.rasterList[i] != None: + if self.parent.rasterList[i]["graphicsItem"].isSelected(): + logger.info("found selected raster") + self.parent.SelectedItemData = self.parent.rasterList[i]["uid"] + self.parent.treeChanged_pv.put(1) + if self.parent.vidActionRasterExploreRadio.isChecked(): + for cell in self.childItems(): + if cell.contains(e.pos()): + if cell.data(0) != None: + if self.currentSelectedCell: + self.currentSelectedCell.setPen(self.parent.redPen) + self.currentSelectedCell.setZValue(0) + spotcount = cell.data(0) + filename = cell.data(1) + d_min = cell.data(2) + intensity = cell.data(3) + if self.parent.staffScreenDialog.albulaDispCheckBox.isChecked(): + if filename != "empty": + logger.debug( + f"filename to display: {filename} spotcount: {spotcount} dmin: {d_min} intensity: {intensity}" + ) + albulaUtils.albulaDispFile(filename) + if not (self.parent.rasterExploreDialog.isVisible()): + self.parent.rasterExploreDialog.show() + self.parent.rasterExploreDialog.setSpotCount(spotcount) + self.parent.rasterExploreDialog.setTotalIntensity(intensity) + self.parent.rasterExploreDialog.setResolution(d_min) + groupList = self.childItems() + cell.setPen(self.parent.yellowPen) + cell.setZValue(1) + self.currentSelectedCell = cell + break + else: + super(RasterGroup, self).mousePressEvent(e) + + def mouseMoveEvent(self, e): + if e.buttons() == QtCore.Qt.LeftButton: + pass + if e.buttons() == QtCore.Qt.RightButton: + pass + + super(RasterGroup, self).mouseMoveEvent(e) + logger.debug(f"pos:{self.pos()} event:{e}") # TODO: Add event description + + def mouseReleaseEvent(self, e): + super(RasterGroup, self).mouseReleaseEvent(e) + if e.button() == QtCore.Qt.LeftButton: + pass + if e.button() == QtCore.Qt.RightButton: + pass + + def hoverMoveEvent(self, e): + super(RasterGroup, self).hoverEnterEvent(e) + for cell in self.childItems(): + if isInCell(e.scenePos(), cell): + if cell.data(0) != None: + spotcount = cell.data(0) + d_min = cell.data(2) + intensity = cell.data(3) + if not (self.parent.rasterExploreDialog.isVisible()): + self.parent.rasterExploreDialog.show() + self.parent.rasterExploreDialog.setSpotCount(spotcount) + self.parent.rasterExploreDialog.setTotalIntensity(intensity) + self.parent.rasterExploreDialog.setResolution(d_min) From 498ee84e53e995e8c81910414789188be3f67ce5 Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra <133771854+NYSBC-Rudra@users.noreply.github.com> Date: Mon, 13 Nov 2023 11:23:25 -0500 Subject: [PATCH 67/79] changing r value edits --- gui/dialog/resolution_dialog.py | 35 ++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/gui/dialog/resolution_dialog.py b/gui/dialog/resolution_dialog.py index f00d6445..88c6c8d8 100644 --- a/gui/dialog/resolution_dialog.py +++ b/gui/dialog/resolution_dialog.py @@ -28,12 +28,18 @@ def __init__(self, parent: "ControlMain"): 'r':{'value':None}} #making lines to hold inputs - self.r_value_enter = QComboBox() - self.r_value_enter.setToolTip("Detector Distance") + # self.r_value_enter = QComboBox() + # self.r_value_enter.setToolTip("Detector Distance") + # self.r_value_enter = QLineEdit() + # self.r_value_enter.setPlaceholderText('Set r value (in mm)') + # self.buttonDictionary['r']['value'] = self.r_value_enter + # self.r_value_enter.setCurrentIndex(1) + + self.r_value_enter = QLineEdit() - self.r_value_enter.setPlaceholderText('Set r value (in mm)') + self.r_value_enter.setPlaceholderText('Set r value') self.buttonDictionary['r']['value'] = self.r_value_enter - self.r_value_enter.setCurrentIndex(1) + self.r_value_enter.setValidator(QDoubleValidator()) #setting inputs to Double only self.L_value_enter = QLineEdit() @@ -106,11 +112,22 @@ def calculateValue(self): return #getting values from textboxes r_value text box - r_value = self.r_value_enter.currentIndex() - convertValues = [200,244.7] - # print("r value = {}".format(r_value)) - #checking if value is a number string or empty string - r_value = convertValues[r_value] + # r_value = self.r_value_enter.currentIndex() + # convertValues = [200,244.7] + # # print("r value = {}".format(r_value)) + # #checking if value is a number string or empty string + # r_value = convertValues[r_value] + # r_value = float(r_value) + + r_value = self.r_value_enter.displayText() + # checking if value is a number string or empty string + if r_value == "" or r_value[0].isalpha() == True: + self.bottom_text.setText("formula to calculate {} requires r value".format(checked_key)) + return + elif float(r_value) < 140 or float(r_value) > 350: + self.bottom_text.setText("r value must be between 140 and 350") + return + r_value = float(r_value) From 5e7a846b084c81281d849ef6fbd00599508dc780 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Mon, 13 Nov 2023 14:38:16 -0500 Subject: [PATCH 68/79] Replaced python calls with json --- daq_main_common.py | 368 ++++++++++++++++++++-------------- gui/control_main.py | 184 +++++++++-------- gui/dialog/screen_defaults.py | 4 +- gui/dialog/staff_screen.py | 59 +++--- gui/dialog/user_screen.py | 21 +- utils/comm.py | 18 ++ 6 files changed, 388 insertions(+), 266 deletions(-) create mode 100644 utils/comm.py diff --git a/daq_main_common.py b/daq_main_common.py index 809301e9..450df504 100755 --- a/daq_main_common.py +++ b/daq_main_common.py @@ -1,186 +1,264 @@ +import _thread +import atexit +import json +import logging +import os import string import sys -import os import time -import _thread +import traceback + +import beamline_lib +import beamline_support +import config_params import daq_lib import daq_utils import det_lib -import beamline_support -import beamline_lib -from start_bs import plt -import config_params -import atexit -import traceback +from beamline_lib import * +from daq_lib import * -#imports to get useful things into namespace for server +# imports to get useful things into namespace for server from daq_macros import * -from daq_lib import * -from robot_lib import * -from beamline_lib import * from gov_lib import setGovRobot +from robot_lib import * +from start_bs import gov_robot, plt -import logging logger = logging.getLogger(__name__) sitefilename = "" -global command_list,immediate_command_list,z +global command_list, immediate_command_list, z command_list = [] immediate_command_list = [] z = 25 - + +def setGovState(state): + setGovRobot(gov_robot, state) + + +functions: "List[Callable]" = [ + anneal, + set_beamsize, + importSpreadsheet, + mvaDescriptor, + setTrans, + loop_center_xrec, + autoRasterLoop, + snakeRaster, + ispybLib.insertRasterResult, + backlightBrighter, + backlightDimmer, + changeImageCenterHighMag, + changeImageCenterLowMag, + center_on_click, + runDCQueue, + warmupGripper, + dryGripper, + enableDewarTscreen, + parkGripper, + stopDCQueue, + continue_data_collection, + mountSample, + unmountSample, + reprocessRaster, + fastDPNodes, + spotNodes, + unmountCold, + openPort, + set_beamcenter, + closePorts, + clearMountedSample, + recoverRobot, + rebootEMBL, + restartEMBL, + openGripper, + closeGripper, + homePins, + setSlit1X, + setSlit1Y, + testRobot, + setGovState, +] + +whitelisted_functions: "Dict[str, Callable]" = { + func.__name__: func for func in functions +} + + def execute_command(command_s): - logger.info('executing command: %s' % command_s) - exec(command_s) + logger.info("executing command: %s" % command_s) + try: + command: "dict[str, Any]" = json.loads(command_s) + func = whitelisted_functions[command["function"]] + func(*command["args"], **command["kwargs"]) + except Exception as e: + logger.error(f"Error executing {command_s}: {e}") def pybass_init(): - global message_string_pv - - daq_utils.init_environment() - daq_lib.init_var_channels() - #if getBlConfig(config_params.DETECTOR_OBJECT_TYPE) == config_params.DETECTOR_OBJECT_TYPE_LSDC: - det_lib.init_detector() - daq_lib.message_string_pv = beamline_support.pvCreate(daq_utils.beamlineComm + "message_string") - daq_lib.gui_popup_message_string_pv = beamline_support.pvCreate(daq_utils.beamlineComm + "gui_popup_message_string") - beamline_lib.read_db() - logger.info("init mots") - beamline_lib.init_mots() #for now - logger.info("init done mots") - daq_lib.init_diffractometer() - try: - sitefilename = os.environ["LSDC_SITE_FILE"] - except KeyError: - gui_message("\$LSDC_SITE_FILE not defined. Questionable defaults in effect.") - if (sitefilename != ""): - if (os.path.exists(sitefilename) == 0): - error_msg = "\$LSDC_SITE_FILE: %s does not exist. Questionable defaults in effect." % sitefilename - gui_message(error_msg) - else: - process_command_file(sitefilename) + global message_string_pv + + daq_utils.init_environment() + daq_lib.init_var_channels() + # if getBlConfig(config_params.DETECTOR_OBJECT_TYPE) == config_params.DETECTOR_OBJECT_TYPE_LSDC: + det_lib.init_detector() + daq_lib.message_string_pv = beamline_support.pvCreate( + daq_utils.beamlineComm + "message_string" + ) + daq_lib.gui_popup_message_string_pv = beamline_support.pvCreate( + daq_utils.beamlineComm + "gui_popup_message_string" + ) + beamline_lib.read_db() + logger.info("init mots") + beamline_lib.init_mots() # for now + logger.info("init done mots") + daq_lib.init_diffractometer() + try: + sitefilename = os.environ["LSDC_SITE_FILE"] + except KeyError: + gui_message("\$LSDC_SITE_FILE not defined. Questionable defaults in effect.") + if sitefilename != "": + if os.path.exists(sitefilename) == 0: + error_msg = ( + "\$LSDC_SITE_FILE: %s does not exist. Questionable defaults in effect." + % sitefilename + ) + gui_message(error_msg) + else: + process_command_file(sitefilename) def process_command_file(command_file_name): - echo_s = "reading %s\n" % command_file_name - logger.info(echo_s) - command_file = open(command_file_name,"r") - while 1: - command = command_file.readline() - if not command: - break - else: - input_tokens = string.split(command) - if (len(input_tokens)>0): - command_string = "%s(" % input_tokens[0] - for i in range(1,len(input_tokens)): - command_string = command_string + "\"" + input_tokens[i] + "\"" - if (i != (len(input_tokens)-1)): - command_string = command_string + "," - command_string = command_string + ")" - logger.info(command_string) - try: - exec(command_string); - except NameError as e: - error_string = "Unknown command in file: %s Error: %s" % (command_string, e) - logger.error(error_string) - except SyntaxError: - logger.error("Syntax error") - except KeyError as e: - logger.error("Key error. Error: %s" % e) - command_file.close() - + echo_s = "reading %s\n" % command_file_name + logger.info(echo_s) + command_file = open(command_file_name, "r") + while 1: + command = command_file.readline() + if not command: + break + else: + input_tokens = string.split(command) + if len(input_tokens) > 0: + command_string = "%s(" % input_tokens[0] + for i in range(1, len(input_tokens)): + command_string = command_string + '"' + input_tokens[i] + '"' + if i != (len(input_tokens) - 1): + command_string = command_string + "," + command_string = command_string + ")" + logger.info(command_string) + try: + exec(command_string) + except NameError as e: + error_string = "Unknown command in file: %s Error: %s" % ( + command_string, + e, + ) + logger.error(error_string) + except SyntaxError: + logger.error("Syntax error") + except KeyError as e: + logger.error("Key error. Error: %s" % e) + command_file.close() def process_immediate_commands(frequency): - while (1): - if (len(immediate_command_list) > 0): - command = immediate_command_list.pop(0) - logger.info('immediate command: %s' % command) - process_input(command) - time.sleep(frequency) + while 1: + if len(immediate_command_list) > 0: + command = immediate_command_list.pop(0) + logger.info("immediate command: %s" % command) + process_input(command) + time.sleep(frequency) + def process_commands(frequency): - while (1): - if (len(command_list) > 0): - command = command_list.pop(0) - logger.info('command: %s' % command) - process_input(command) + while 1: + if len(command_list) > 0: + command = command_list.pop(0) + logger.info("command: %s" % command) + process_input(command) + - def print_status_thread(frequency): - global count_list,ring_intensity - - previous_image_started = 0 - percent_done = 0 - shutter_dead_time = .6 - while 1: - time.sleep(frequency) - current_percent_done = daq_lib.get_field("state_percent") - if (daq_lib.image_started > 0): - if (start_time == 0 or daq_lib.image_started != previous_image_started): - previous_image_started = daq_lib.image_started - start_time = time.time() - now = time.time() - total_time = float(daq_lib.image_started) - if (total_time>0.0): - percent_done = int(.5+((now-start_time)/total_time*100)) - else: - percent_done = 0 - else: - start_time = 0 - percent_done = 0 - if (percent_done != current_percent_done): - daq_lib.set_field("state_percent",percent_done) + global count_list, ring_intensity + + previous_image_started = 0 + percent_done = 0 + shutter_dead_time = 0.6 + while 1: + time.sleep(frequency) + current_percent_done = daq_lib.get_field("state_percent") + if daq_lib.image_started > 0: + if start_time == 0 or daq_lib.image_started != previous_image_started: + previous_image_started = daq_lib.image_started + start_time = time.time() + now = time.time() + total_time = float(daq_lib.image_started) + if total_time > 0.0: + percent_done = int(0.5 + ((now - start_time) / total_time * 100)) + else: + percent_done = 0 + else: + start_time = 0 + percent_done = 0 + if percent_done != current_percent_done: + daq_lib.set_field("state_percent", percent_done) def comm_cb(value=None, char_value=None, **kw): - command = char_value - command_list.append(command) - + command = char_value + command_list.append(command) + + def comm2_cb(value=None, char_value=None, **kw): - command = char_value - if not (command == "\n"): - immediate_command_list.append(command) - + command = char_value + if not (command == "\n"): + immediate_command_list.append(command) def process_input(command_string): - if (command_string == ""): - return - if (command_string == "q"): - sys.exit() - daq_lib.broadcast_output(time.ctime(time.time()) + "\n" + command_string) - try: - daq_lib.set_field("program_state","Program Busy") - execute_command(command_string) - except NameError as e: - error_string = "Unknown command in queue: %s Error: %s" % (command_string, e) - logger.error(error_string) - exc_type, exc_value, exc_traceback = sys.exc_info() - print("*** print_tb:") - traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) - except SyntaxError: - logger.exception("Syntax error") - except KeyError as e: - logger.exception("Key error. Error: %s. Command was: %s" % (e, command_string)) - except TypeError as e: - logger.exception("Type error. Error: %s" % e) - except AttributeError as e: - logger.error("Attribute Error: %s" % e) - except KeyboardInterrupt: - abort_data_collection() - logger.info("Interrupt caught by daq server\n") - if (command_string != "pause_data_collection()" and command_string != "continue_data_collection()" and command_string != "abort_data_collection()" and command_string != "unmount_after_abort()" and command_string != "no_unmount_after_abort()"): - daq_lib.set_field("program_state","Program Ready") + if command_string == "": + return + if command_string == "q": + sys.exit() + daq_lib.broadcast_output(time.ctime(time.time()) + "\n" + command_string) + try: + daq_lib.set_field("program_state", "Program Busy") + execute_command(command_string) + except NameError as e: + error_string = "Unknown command in queue: %s Error: %s" % (command_string, e) + logger.error(error_string) + exc_type, exc_value, exc_traceback = sys.exc_info() + print("*** print_tb:") + traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) + except SyntaxError: + logger.exception("Syntax error") + except KeyError as e: + logger.exception("Key error. Error: %s. Command was: %s" % (e, command_string)) + except TypeError as e: + logger.exception("Type error. Error: %s" % e) + except AttributeError as e: + logger.error("Attribute Error: %s" % e) + except KeyboardInterrupt: + abort_data_collection() + logger.info("Interrupt caught by daq server\n") + if ( + command_string != "pause_data_collection()" + and command_string != "continue_data_collection()" + and command_string != "abort_data_collection()" + and command_string != "unmount_after_abort()" + and command_string != "no_unmount_after_abort()" + ): + daq_lib.set_field("program_state", "Program Ready") def run_server(): - _thread.start_new_thread(process_immediate_commands,(.25,)) - comm_pv = beamline_support.pvCreate(daq_utils.beamlineComm + "command_s") - beamline_support.pvPut(comm_pv,"\n") - immediate_comm_pv = beamline_support.pvCreate(daq_utils.beamlineComm + "immediate_command_s") - beamline_support.pvPut(immediate_comm_pv,"\n") - comm_pv.add_callback(comm_cb) - immediate_comm_pv.add_callback(comm2_cb) - process_commands(0.5) + _thread.start_new_thread(process_immediate_commands, (0.25,)) + comm_pv = beamline_support.pvCreate(daq_utils.beamlineComm + "command_s") + beamline_support.pvPut(comm_pv, "\n") + immediate_comm_pv = beamline_support.pvCreate( + daq_utils.beamlineComm + "immediate_command_s" + ) + beamline_support.pvPut(immediate_comm_pv, "\n") + comm_pv.add_callback(comm_cb) + immediate_comm_pv.add_callback(comm2_cb) + process_commands(0.5) diff --git a/gui/control_main.py b/gui/control_main.py index bb2ea0a3..7ed62141 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -17,7 +17,7 @@ from qtpy import QtCore, QtGui, QtWidgets from qtpy.QtCore import QModelIndex, QRectF, Qt, QTimer from qtpy.QtGui import QIntValidator -from qtpy.QtWidgets import QCheckBox, QFrame, QGraphicsPixmapItem, QApplication +from qtpy.QtWidgets import QApplication, QCheckBox, QFrame, QGraphicsPixmapItem import albulaUtils import daq_utils @@ -26,9 +26,9 @@ from config_params import ( CRYOSTREAM_ONLINE, HUTCH_TIMER_DELAY, - SERVER_CHECK_DELAY, RASTER_GUI_XREC_FILL_DELAY, SAMPLE_TIMER_DELAY, + SERVER_CHECK_DELAY, VALID_DET_DIST, VALID_EXP_TIMES, VALID_TOTAL_EXP_TIMES, @@ -51,10 +51,12 @@ ) from gui.raster import RasterCell, RasterGroup from QPeriodicTable import QPeriodicTable -from threads import RaddoseThread, VideoThread, ServerCheckThread +from threads import RaddoseThread, ServerCheckThread, VideoThread +from utils.comm import generate_server_message logger = logging.getLogger() + def get_request_object_escan( reqObj, symbol, @@ -230,7 +232,7 @@ def __init__(self): ] self.mountedPin_pv.put(mountedPin) self.rasterExploreDialog = RasterExploreDialog() - + self.detDistMotorEntry.getEntry().setText( self.detDistRBVLabel.getEntry().text() ) # this is to fix the current val being overwritten by reso @@ -240,7 +242,7 @@ def __init__(self): self.changeControlMasterCB(1) self.controlMasterCheckBox.setChecked(True) self.XRFInfoDict = self.parseXRFTable() # I don't like this - #self.dewarTree.refreshTreeDewarView() + # self.dewarTree.refreshTreeDewarView() def setGuiValues(self, values): for item, value in values.items(): @@ -404,7 +406,7 @@ def createSampleTab(self): self.osc_end_ledit.textChanged[str].connect( functools.partial(self.totalExpChanged, "oscEnd") ) - if daq_utils.beamline == "fmx": + if daq_utils.beamline == "fmx": self.osc_end_ledit.textChanged.connect(self.calcLifetimeCB) hBoxColParams1.addWidget(colStartLabel) hBoxColParams1.addWidget(self.osc_start_ledit) @@ -1407,8 +1409,7 @@ def createSampleTab(self): lambda frame: self.updateCam(self.pixmap_item_HutchTop, frame) ) self.hutchTopCamThread.start() - serverCheckThread = ServerCheckThread( - parent=self, delay=SERVER_CHECK_DELAY) + serverCheckThread = ServerCheckThread(parent=self, delay=SERVER_CHECK_DELAY) serverCheckThread.visit_dir_changed.connect(QApplication.instance().quit) serverCheckThread.start() @@ -1425,7 +1426,7 @@ def annealButtonCB(self): try: ftime = float(self.annealTime_ledit.text()) if ftime >= 0.1 and ftime <= 5.0: - comm_s = "anneal(" + str(ftime) + ")" + comm_s = generate_server_message("anneal", [ftime]) logger.info(comm_s) self.send_to_server(comm_s) else: @@ -1703,18 +1704,12 @@ def saveVidSnapshotCB( if rasterHeatJpeg == None: if reqID != None: filePrefix = db_lib.getRequestByID(reqID)["request_obj"]["file_prefix"] - imagePath = ( - f"{getBlConfig('visitDirectory')}/snapshots/{filePrefix}{int(now)}.jpg" - ) + imagePath = f"{getBlConfig('visitDirectory')}/snapshots/{filePrefix}{int(now)}.jpg" else: if self.dataPathGB.prefix_ledit.text() != "": - imagePath = ( - f"{getBlConfig('visitDirectory')}/snapshots/{self.dataPathGB.prefix_ledit.text()}{int(now)}.jpg" - ) + imagePath = f"{getBlConfig('visitDirectory')}/snapshots/{self.dataPathGB.prefix_ledit.text()}{int(now)}.jpg" else: - imagePath = ( - f"{getBlConfig('visitDirectory')}/snapshots/capture{int(now)}.jpg" - ) + imagePath = f"{getBlConfig('visitDirectory')}/snapshots/capture{int(now)}.jpg" else: imagePath = rasterHeatJpeg logger.info("saving " + imagePath) @@ -2503,7 +2498,7 @@ def protoRadioToggledCB(self, text): pass def beamsizeComboActivatedCB(self, text): - comm_s = 'set_beamsize("' + str(text[0:2]) + '","' + str(text[2:4]) + '")' + comm_s = generate_server_message("set_beamsize", [text[0:2], text[2:4]]) logger.info(comm_s) self.send_to_server(comm_s) @@ -2611,7 +2606,9 @@ def popImportDialogCB(self): self.timerSample.start(SAMPLE_TIMER_DELAY) if fname != "": logger.info(fname) - comm_s = f'importSpreadsheet("{fname[0]}", "{daq_utils.owner}")' + comm_s = generate_server_message( + "importSpreadsheet", [fname[0], daq_utils.owner] + ) logger.info(comm_s) self.send_to_server(comm_s) @@ -2696,10 +2693,9 @@ def dewarViewToggleCheckCB(self): self.dewarTree.refreshTreePriorityView() def moveOmegaCB(self): - comm_s = ( - 'mvaDescriptor("omega",' - + str(self.sampleOmegaMoveLedit.getEntry().text()) - + ")" + comm_s = generate_server_message( + "mvaDescriptor", + ["omega", float(self.sampleOmegaMoveLedit.getEntry().text())], ) logger.info(comm_s) self.send_to_server(comm_s) @@ -2710,7 +2706,9 @@ def moveEnergyCB(self): self.popupServerMessage("Energy change must be less than 10 ev") return else: - comm_s = 'mvaDescriptor("energy",' + str(self.energy_ledit.text()) + ")" + comm_s = generate_server_message( + "mvaDescriptor", ["energy", float(self.energy_ledit.text())] + ) logger.info(comm_s) self.send_to_server(comm_s) @@ -2747,7 +2745,7 @@ def spawnRaddoseThread(self): vecLen = float(self.vecLenLabelOutput.text()) except: pass - + try: wedge = float(self.osc_end_ledit.text()) raddose_thread = RaddoseThread( @@ -2779,7 +2777,9 @@ def setTransCB(self): except ValueError as e: self.popupServerMessage("Please enter a valid number") return - comm_s = "setTrans(" + str(self.transmission_ledit.text()) + ")" + comm_s = generate_server_message( + "setTrans", ["energy", float(self.transmission_ledit.text())] + ) logger.info(comm_s) self.send_to_server(comm_s) @@ -2788,10 +2788,12 @@ def setDCStartCB(self): self.setGuiValues({"osc_start": currentPos}) def moveDetDistCB(self): - comm_s = ( - 'mvaDescriptor("detectorDist",' - + str(self.detDistMotorEntry.getEntry().text()) - + ")" + comm_s = generate_server_message( + "mvaDescriptor", + [ + "detectorDist", + float(self.detDistMotorEntry.getEntry().text()), + ], ) logger.info(comm_s) self.send_to_server(comm_s) @@ -2851,15 +2853,18 @@ def omegaTweakCB(self, tv): def autoCenterLoopCB(self): logger.info("auto center loop") - self.send_to_server("loop_center_xrec()") + comm_s = generate_server_message("loop_center_xrec") + self.send_to_server(comm_s) def autoRasterLoopCB(self): self.selectedSampleID = self.selectedSampleRequest["sample"] - comm_s = "autoRasterLoop(" + str(self.selectedSampleID) + ")" + comm_s = generate_server_message("autoRasterLoop", [self.selectedSampleID]) self.send_to_server(comm_s) def runRastersCB(self): - comm_s = "snakeRaster(" + str(self.selectedSampleRequest["uid"]) + ")" + comm_s = generate_server_message( + "snakeRaster", [self.selectedSampleRequest["uid"]] + ) self.send_to_server(comm_s) def drawInteractiveRasterCB(self): # any polygon for now, interactive or from xrec @@ -2936,7 +2941,8 @@ def center3LoopCB(self): logger.info("3-click center loop") self.threeClickCount = 1 self.click3Button.setStyleSheet("background-color: yellow") - self.send_to_server('mvaDescriptor("omega",0)') + comm_s = generate_server_message("mvaDescriptor", ["omega", 0]) + self.send_to_server(comm_s) def fillPolyRaster( self, rasterReq, waitTime=1 @@ -3113,7 +3119,10 @@ def takeRasterSnapshot(self, rasterReq): reqID=rasterReq["uid"], rasterHeatJpeg=jpegImageFilename, ) - self.send_to_server(f"ispybLib.insertRasterResult('{rasterReq['uid']}', '{visitName}')") + comm_s = generate_server_message( + "ispybLib.insertRasterResult", [str(rasterReq["uid"]), str(visitName)] + ) + self.send_to_server(comm_s) def reFillPolyRaster(self): rasterEvalOption = str(self.rasterEvalComboBox.currentText()) @@ -3212,10 +3221,12 @@ def selectAllCenterCB(self): self.centeringMarksList[i]["graphicsItem"].setSelected(True) def lightUpCB(self): - self.send_to_server("backlightBrighter()") + comm_s = generate_server_message("backlightBrighter") + self.send_to_server(comm_s) def lightDimCB(self): - self.send_to_server("backlightDimmer()") + comm_s = generate_server_message("backlightDimmer") + self.send_to_server(comm_s) def eraseRastersCB(self): if self.rasterList != []: @@ -3571,36 +3582,20 @@ def pixelSelect(self, event): True ) # because it's easy to forget defineCenter is on if self.zoom4Radio.isChecked(): - comm_s = ( - "changeImageCenterHighMag(" - + str(x_click) - + "," - + str(y_click) - + ",1)" + comm_s = generate_server_message( + "changeImageCenterHighMag", [x_click, y_click, 1] ) elif self.zoom3Radio.isChecked(): - comm_s = ( - "changeImageCenterHighMag(" - + str(x_click) - + "," - + str(y_click) - + ",0)" + comm_s = generate_server_message( + "changeImageCenterHighMag", [x_click, y_click, 0] ) if self.zoom2Radio.isChecked(): - comm_s = ( - "changeImageCenterLowMag(" - + str(x_click) - + "," - + str(y_click) - + ",1)" + comm_s = generate_server_message( + "changeImageCenterLowMag", [x_click, y_click, 1] ) elif self.zoom1Radio.isChecked(): - comm_s = ( - "changeImageCenterLowMag(" - + str(x_click) - + "," - + str(y_click) - + ",0)" + comm_s = generate_server_message( + "changeImageCenterLowMag", [x_click, y_click, 0] ) self.send_to_server(comm_s) return @@ -3630,9 +3625,27 @@ def pixelSelect(self, event): if self.threeClickCount > 0: # 3-click centering self.threeClickCount = self.threeClickCount + 1 - comm_s = f'center_on_click({correctedC2C_x},{correctedC2C_y},{fov["x"]},{fov["y"]},source="screen",jog=90,viewangle={current_viewangle})' + comm_s = generate_server_message( + "center_on_click", + [ + correctedC2C_x, + correctedC2C_y, + fov["x"], + fov["y"], + {"source": "screen", "jog": 90, "viewangle": current_viewangle}, + ], + ) else: - comm_s = f'center_on_click({correctedC2C_x},{correctedC2C_y},{fov["x"]},{fov["y"]},source="screen",maglevel=0,viewangle={current_viewangle})' + comm_s = generate_server_message( + "center_on_click", + [ + correctedC2C_x, + correctedC2C_y, + fov["x"], + fov["y"], + {"source": "screen", "maglevel": 0, "viewangle": current_viewangle}, + ], + ) if not self.vidActionRasterExploreRadio.isChecked(): self.aux_send_to_server(comm_s) if self.threeClickCount == 4: @@ -4193,19 +4206,24 @@ def collectQueueCB(self): if currentRequest == {}: self.addRequestsToAllSelectedCB() logger.info("running queue") - self.send_to_server("runDCQueue()") + comm_s = generate_server_message("runDCQueue") + self.send_to_server(comm_s) def warmupGripperCB(self): - self.send_to_server("warmupGripper()") + comm_s = generate_server_message("warmupGripper") + self.send_to_server(comm_s) def dryGripperCB(self): - self.send_to_server("dryGripper()") + comm_s = generate_server_message("dryGripper") + self.send_to_server(comm_s) def enableTScreenGripperCB(self): - self.send_to_server("enableDewarTscreen()") + comm_s = generate_server_message("enableDewarTscreen") + self.send_to_server(comm_s) def parkGripperCB(self): - self.send_to_server("parkGripper()") + comm_s = generate_server_message("parkGripper") + self.send_to_server(comm_s) def restartServerCB(self): if self.controlEnabled(): @@ -4436,14 +4454,17 @@ def puckToDewarCB(self): def stopRunCB(self): logger.info("stopping collection") - self.aux_send_to_server("stopDCQueue(1)") + comm_s = generate_server_message("stopDCQueue", [1]) + self.aux_send_to_server(comm_s) def stopQueueCB(self): logger.info("stopping queue") if self.pauseQueueButton.text() == "Continue": - self.aux_send_to_server("continue_data_collection()") + comm_s = generate_server_message("continue_data_collection") + self.aux_send_to_server(comm_s) else: - self.aux_send_to_server("stopDCQueue(2)") + comm_s = generate_server_message("stopDCQueue", [2]) + self.aux_send_to_server(comm_s) def mountSampleCB(self): if getBlConfig("mountEnabled") == 0: @@ -4458,7 +4479,8 @@ def mountSampleCB(self): else: # No sample ID found, do nothing logger.info("No sample selected, cannot mount") return - self.send_to_server('mountSample("' + str(self.selectedSampleID) + '")') + comm_s = generate_server_message("mountSample", [self.selectedSampleID]) + self.send_to_server(comm_s) self.zoom2Radio.setChecked(True) self.zoomLevelToggledCB("Zoom2") self.protoComboBox.setCurrentIndex(self.protoComboBox.findText(str("standard"))) @@ -4466,7 +4488,8 @@ def mountSampleCB(self): def unmountSampleCB(self): logger.info("unmount sample") - self.send_to_server("unmountSample()") + comm_s = generate_server_message("unmountSample") + self.send_to_server(comm_s) def refreshCollectionParams(self, selectedSampleRequest, validate_hdf5=True): reqObj = selectedSampleRequest["request_obj"] @@ -4581,12 +4604,13 @@ def refreshCollectionParams(self, selectedSampleRequest, validate_hdf5=True): ) > 5.0 ): - comm_s = ( - 'mvaDescriptor("omega",' - + str( - selectedSampleRequest["request_obj"]["rasterDef"]["omega"] - ) - + ")" + + comm_s = generate_server_message( + "mvaDescriptor", + [ + "omega", + selectedSampleRequest["request_obj"]["rasterDef"]["omega"], + ], ) self.send_to_server(comm_s) if str(reqObj["protocol"]) == "eScan": diff --git a/gui/dialog/screen_defaults.py b/gui/dialog/screen_defaults.py index a5f548c8..7dcc743a 100644 --- a/gui/dialog/screen_defaults.py +++ b/gui/dialog/screen_defaults.py @@ -14,6 +14,7 @@ VALID_EXP_TIMES, ) from daq_utils import beamline, getBlConfig, setBlConfig +from utils.comm import generate_server_message if typing.TYPE_CHECKING: from lsdcGui import ControlMain @@ -202,7 +203,8 @@ def reprocessRasterRequestCB(self): try: reqID = self.parent.selectedSampleRequest["uid"] self.parent.drawPolyRaster(db_lib.getRequestByID(reqID)) - self.parent.send_to_server('reprocessRaster("' + str(reqID) + '")') + comm_s = generate_server_message("reprocessRaster", [reqID]) + self.parent.send_to_server(comm_s) except: pass diff --git a/gui/dialog/staff_screen.py b/gui/dialog/staff_screen.py index 83f41023..07a5f7d5 100644 --- a/gui/dialog/staff_screen.py +++ b/gui/dialog/staff_screen.py @@ -6,6 +6,7 @@ from config_params import BEAM_CHECK, TOP_VIEW_CHECK, UNMOUNT_COLD_CHECK from daq_utils import getBlConfig, setBlConfig +from utils.comm import generate_server_message if typing.TYPE_CHECKING: from lsdcGui import ControlMain @@ -222,13 +223,13 @@ def getFastDPNodeList(self): return nodeList def setFastDPNodesCB(self): - comm_s = "fastDPNodes(" - for i in range(0, self.fastDPNodeCount): - comm_s = comm_s + str(self.fastDPNodeEntryList[i].text()) - if i == self.fastDPNodeCount - 1: - comm_s = comm_s + ")" - else: - comm_s = comm_s + "," + comm_s = generate_server_message( + "fastDPNodes", + [ + int(self.fastDPNodeEntryList[i].text()) + for i in range(self.fastDPNodeCount) + ], + ) logger.info(comm_s) self.parent.send_to_server(comm_s) @@ -239,54 +240,52 @@ def unLockGuiCB(self): self.parent.send_to_server("unlockControl") def setSpotNodesCB(self): - comm_s = "spotNodes(" - for i in range(0, self.spotNodeCount): - comm_s = comm_s + str(self.spotNodeEntryList[i].text()) - if i == self.spotNodeCount - 1: - comm_s = comm_s + ")" - else: - comm_s = comm_s + "," + comm_s = generate_server_message( + "spotNodes", + [int(self.spotNodeEntryList[i].text()) for i in range(self.spotNodeCount)], + ) logger.info(comm_s) self.parent.send_to_server(comm_s) def unmountColdCB(self): - self.parent.send_to_server("unmountCold()") + comm_s = generate_server_message("unmountCold") + self.parent.send_to_server(comm_s) def openPort1CB(self): - self.parent.send_to_server("openPort(1)") + comm_s = generate_server_message("openPort", [1]) + self.parent.send_to_server(comm_s) def setBeamcenterCB(self): - self.parent.send_to_server( - "set_beamcenter (" - + str(self.beamcenterX_ledit.text()) - + "," - + str(self.beamcenterY_ledit.text()) - + ")" + comm_s = generate_server_message( + "set_beamcenter", + [self.beamcenterX_ledit.text(), self.beamcenterY_ledit.text()], ) + self.parent.send_to_server(comm_s) + def closePortsCB(self): - self.parent.send_to_server("closePorts()") + self.parent.send_to_server(generate_server_message("closePorts")) def clearMountedSampleCB(self): - self.parent.send_to_server("clearMountedSample()") + self.parent.send_to_server(generate_server_message("clearMountedSample")) def recoverRobotCB(self): - self.parent.aux_send_to_server("recoverRobot()") + self.parent.aux_send_to_server(generate_server_message("recoverRobot")) def rebootEMBL_CB(self): - self.parent.aux_send_to_server("rebootEMBL()") + self.parent.aux_send_to_server(generate_server_message("rebootEMBL")) def restartEMBL_CB(self): - self.parent.send_to_server("restartEMBL()") + self.parent.send_to_server(generate_server_message("restartEMBL")) def openGripper_CB(self): - self.parent.send_to_server("openGripper()") + self.parent.send_to_server(generate_server_message("openGripper")) def closeGripper_CB(self): - self.parent.send_to_server("closeGripper()") + self.parent.send_to_server(generate_server_message("closeGripper")) def homePinsCB(self): - self.parent.send_to_server("homePins()") + self.parent.send_to_server(generate_server_message("homePins")) def robotOnCheckCB(self, state): if state == QtCore.Qt.Checked: diff --git a/gui/dialog/user_screen.py b/gui/dialog/user_screen.py index e4c0ef20..f8b9b5cf 100644 --- a/gui/dialog/user_screen.py +++ b/gui/dialog/user_screen.py @@ -6,6 +6,7 @@ from qtpy.QtWidgets import QCheckBox import daq_utils +from utils.comm import generate_server_message if typing.TYPE_CHECKING: from lsdcGui import ControlMain @@ -197,24 +198,24 @@ def __init__(self, parent: "ControlMain"): self.setLayout(vBoxColParams1) def setSlit1XCB(self): - comm_s = "setSlit1X(" + str(self.slit1XMotor_ledit.text()) + ")" + comm_s = generate_server_message("setSlit1X", [self.slit1XMotor_ledit.text()]) self.parent.send_to_server(comm_s) def setSlit1YCB(self): - comm_s = "setSlit1Y(" + str(self.slit1YMotor_ledit.text()) + ")" + comm_s = generate_server_message("setSlit1Y", [self.slit1YMotor_ledit.text()]) self.parent.send_to_server(comm_s) def unmountColdCB(self): - self.parent.send_to_server("unmountCold()") + self.parent.send_to_server(generate_server_message("unmountCold")) def testRobotCB(self): - self.parent.send_to_server("testRobot()") + self.parent.send_to_server(generate_server_message("testRobot")) def recoverRobotCB(self): - self.parent.send_to_server("recoverRobot()") + self.parent.send_to_server(generate_server_message("recoverRobot")) def dryGripperCB(self): - self.parent.send_to_server("dryGripper()") + self.parent.send_to_server(generate_server_message("dryGripper")) def stopDetCB(self): logger.info("stopping detector") @@ -235,16 +236,16 @@ def rebootZebraIOC_CB(self): self.parent.rebootZebraIOC_pv.put(1) def SEgovCB(self): - self.parent.send_to_server("setGovRobot(gov_robot, 'SE')") + self.parent.send_to_server(generate_server_message("setGovState", ["SE"])) def SAgovCB(self): - self.parent.send_to_server("setGovRobot(gov_robot, 'SA')") + self.parent.send_to_server(generate_server_message("setGovState", ["SA"])) def DAgovCB(self): - self.parent.send_to_server("setGovRobot(gov_robot, 'DA')") + self.parent.send_to_server(generate_server_message("setGovState", ["DA"])) def BLgovCB(self): - self.parent.send_to_server("setGovRobot(gov_robot, 'BL')") + self.parent.send_to_server(generate_server_message("setGovState", ["BL"])) def userScreenOKCB(self): self.hide() diff --git a/utils/comm.py b/utils/comm.py new file mode 100644 index 00000000..0cc41898 --- /dev/null +++ b/utils/comm.py @@ -0,0 +1,18 @@ +import json +from typing import Dict, List, Optional + + +def generate_server_message( + function_name: str, args: Optional[List] = None, kwargs: Optional[Dict] = None +) -> str: + if not args: + args = [] + if not kwargs: + kwargs = {} + return json.dumps( + { + "function": function_name, + "args": args, + "kwargs": kwargs, + } + ) From a3bde35dfce5bc75573ebae5ac29e7e02f80e19b Mon Sep 17 00:00:00 2001 From: Shekar V Date: Mon, 13 Nov 2023 15:18:00 -0500 Subject: [PATCH 69/79] Removed code formatting in daq_main_common --- daq_main_common.py | 386 +++++++++++++++++++++------------------------ 1 file changed, 182 insertions(+), 204 deletions(-) diff --git a/daq_main_common.py b/daq_main_common.py index 450df504..bdf8b303 100755 --- a/daq_main_common.py +++ b/daq_main_common.py @@ -17,7 +17,7 @@ from beamline_lib import * from daq_lib import * -# imports to get useful things into namespace for server +#imports to get useful things into namespace for server from daq_macros import * from gov_lib import setGovRobot from robot_lib import * @@ -26,239 +26,217 @@ logger = logging.getLogger(__name__) sitefilename = "" -global command_list, immediate_command_list, z +global command_list,immediate_command_list,z command_list = [] immediate_command_list = [] z = 25 - def setGovState(state): - setGovRobot(gov_robot, state) - - -functions: "List[Callable]" = [ - anneal, - set_beamsize, - importSpreadsheet, - mvaDescriptor, - setTrans, - loop_center_xrec, - autoRasterLoop, - snakeRaster, - ispybLib.insertRasterResult, - backlightBrighter, - backlightDimmer, - changeImageCenterHighMag, - changeImageCenterLowMag, - center_on_click, - runDCQueue, - warmupGripper, - dryGripper, - enableDewarTscreen, - parkGripper, - stopDCQueue, - continue_data_collection, - mountSample, - unmountSample, - reprocessRaster, - fastDPNodes, - spotNodes, - unmountCold, - openPort, - set_beamcenter, - closePorts, - clearMountedSample, - recoverRobot, - rebootEMBL, - restartEMBL, - openGripper, - closeGripper, - homePins, - setSlit1X, - setSlit1Y, - testRobot, - setGovState, -] + setGovRobot(gov_robot, state) + + +functions = [anneal, + set_beamsize, + importSpreadsheet, + mvaDescriptor, + setTrans, + loop_center_xrec, + autoRasterLoop, + snakeRaster, + ispybLib.insertRasterResult, + backlightBrighter, + backlightDimmer, + changeImageCenterHighMag, + changeImageCenterLowMag, + center_on_click, + runDCQueue, + warmupGripper, + dryGripper, + enableDewarTscreen, + parkGripper, + stopDCQueue, + continue_data_collection, + mountSample, + unmountSample, + reprocessRaster, + fastDPNodes, + spotNodes, + unmountCold, + openPort, + set_beamcenter, + closePorts, + clearMountedSample, + recoverRobot, + rebootEMBL, + restartEMBL, + openGripper, + closeGripper, + homePins, + setSlit1X, + setSlit1Y, + testRobot, + setGovState] whitelisted_functions: "Dict[str, Callable]" = { func.__name__: func for func in functions } - - + def execute_command(command_s): - logger.info("executing command: %s" % command_s) - try: - command: "dict[str, Any]" = json.loads(command_s) - func = whitelisted_functions[command["function"]] - func(*command["args"], **command["kwargs"]) - except Exception as e: - logger.error(f"Error executing {command_s}: {e}") + logger.info("executing command: %s" % command_s) + try: + command: "dict[str, Any]" = json.loads(command_s) + func = whitelisted_functions[command["function"]] + func(*command["args"], **command["kwargs"]) + except Exception as e: + logger.error(f"Error executing {command_s}: {e}") def pybass_init(): - global message_string_pv - - daq_utils.init_environment() - daq_lib.init_var_channels() - # if getBlConfig(config_params.DETECTOR_OBJECT_TYPE) == config_params.DETECTOR_OBJECT_TYPE_LSDC: - det_lib.init_detector() - daq_lib.message_string_pv = beamline_support.pvCreate( - daq_utils.beamlineComm + "message_string" - ) - daq_lib.gui_popup_message_string_pv = beamline_support.pvCreate( - daq_utils.beamlineComm + "gui_popup_message_string" - ) - beamline_lib.read_db() - logger.info("init mots") - beamline_lib.init_mots() # for now - logger.info("init done mots") - daq_lib.init_diffractometer() - try: - sitefilename = os.environ["LSDC_SITE_FILE"] - except KeyError: - gui_message("\$LSDC_SITE_FILE not defined. Questionable defaults in effect.") - if sitefilename != "": - if os.path.exists(sitefilename) == 0: - error_msg = ( - "\$LSDC_SITE_FILE: %s does not exist. Questionable defaults in effect." - % sitefilename - ) - gui_message(error_msg) - else: - process_command_file(sitefilename) + global message_string_pv + + daq_utils.init_environment() + daq_lib.init_var_channels() + #if getBlConfig(config_params.DETECTOR_OBJECT_TYPE) == config_params.DETECTOR_OBJECT_TYPE_LSDC: + det_lib.init_detector() + daq_lib.message_string_pv = beamline_support.pvCreate(daq_utils.beamlineComm + "message_string") + daq_lib.gui_popup_message_string_pv = beamline_support.pvCreate(daq_utils.beamlineComm + "gui_popup_message_string") + beamline_lib.read_db() + logger.info("init mots") + beamline_lib.init_mots() #for now + logger.info("init done mots") + daq_lib.init_diffractometer() + try: + sitefilename = os.environ["LSDC_SITE_FILE"] + except KeyError: + gui_message("\$LSDC_SITE_FILE not defined. Questionable defaults in effect.") + if (sitefilename != ""): + if (os.path.exists(sitefilename) == 0): + error_msg = "\$LSDC_SITE_FILE: %s does not exist. Questionable defaults in effect." % sitefilename + gui_message(error_msg) + else: + process_command_file(sitefilename) def process_command_file(command_file_name): - echo_s = "reading %s\n" % command_file_name - logger.info(echo_s) - command_file = open(command_file_name, "r") - while 1: - command = command_file.readline() - if not command: - break - else: - input_tokens = string.split(command) - if len(input_tokens) > 0: - command_string = "%s(" % input_tokens[0] - for i in range(1, len(input_tokens)): - command_string = command_string + '"' + input_tokens[i] + '"' - if i != (len(input_tokens) - 1): - command_string = command_string + "," - command_string = command_string + ")" - logger.info(command_string) - try: - exec(command_string) - except NameError as e: - error_string = "Unknown command in file: %s Error: %s" % ( - command_string, - e, - ) - logger.error(error_string) - except SyntaxError: - logger.error("Syntax error") - except KeyError as e: - logger.error("Key error. Error: %s" % e) - command_file.close() + echo_s = "reading %s\n" % command_file_name + logger.info(echo_s) + command_file = open(command_file_name,"r") + while 1: + command = command_file.readline() + if not command: + break + else: + input_tokens = string.split(command) + if (len(input_tokens)>0): + command_string = "%s(" % input_tokens[0] + for i in range(1,len(input_tokens)): + command_string = command_string + "\"" + input_tokens[i] + "\"" + if (i != (len(input_tokens)-1)): + command_string = command_string + "," + command_string = command_string + ")" + logger.info(command_string) + try: + exec(command_string); + except NameError as e: + error_string = "Unknown command in file: %s Error: %s" % (command_string, e) + logger.error(error_string) + except SyntaxError: + logger.error("Syntax error") + except KeyError as e: + logger.error("Key error. Error: %s" % e) + command_file.close() + def process_immediate_commands(frequency): - while 1: - if len(immediate_command_list) > 0: - command = immediate_command_list.pop(0) - logger.info("immediate command: %s" % command) - process_input(command) - time.sleep(frequency) - + while (1): + if (len(immediate_command_list) > 0): + command = immediate_command_list.pop(0) + logger.info('immediate command: %s' % command) + process_input(command) + time.sleep(frequency) def process_commands(frequency): - while 1: - if len(command_list) > 0: - command = command_list.pop(0) - logger.info("command: %s" % command) - process_input(command) - + while (1): + if (len(command_list) > 0): + command = command_list.pop(0) + logger.info('command: %s' % command) + process_input(command) + def print_status_thread(frequency): - global count_list, ring_intensity - - previous_image_started = 0 - percent_done = 0 - shutter_dead_time = 0.6 - while 1: - time.sleep(frequency) - current_percent_done = daq_lib.get_field("state_percent") - if daq_lib.image_started > 0: - if start_time == 0 or daq_lib.image_started != previous_image_started: - previous_image_started = daq_lib.image_started - start_time = time.time() - now = time.time() - total_time = float(daq_lib.image_started) - if total_time > 0.0: - percent_done = int(0.5 + ((now - start_time) / total_time * 100)) - else: - percent_done = 0 - else: - start_time = 0 - percent_done = 0 - if percent_done != current_percent_done: - daq_lib.set_field("state_percent", percent_done) + global count_list,ring_intensity + + previous_image_started = 0 + percent_done = 0 + shutter_dead_time = .6 + while 1: + time.sleep(frequency) + current_percent_done = daq_lib.get_field("state_percent") + if (daq_lib.image_started > 0): + if (start_time == 0 or daq_lib.image_started != previous_image_started): + previous_image_started = daq_lib.image_started + start_time = time.time() + now = time.time() + total_time = float(daq_lib.image_started) + if (total_time>0.0): + percent_done = int(.5+((now-start_time)/total_time*100)) + else: + percent_done = 0 + else: + start_time = 0 + percent_done = 0 + if (percent_done != current_percent_done): + daq_lib.set_field("state_percent",percent_done) def comm_cb(value=None, char_value=None, **kw): - command = char_value - command_list.append(command) - - + command = char_value + command_list.append(command) + def comm2_cb(value=None, char_value=None, **kw): - command = char_value - if not (command == "\n"): - immediate_command_list.append(command) + command = char_value + if not (command == "\n"): + immediate_command_list.append(command) + def process_input(command_string): - if command_string == "": - return - if command_string == "q": - sys.exit() - daq_lib.broadcast_output(time.ctime(time.time()) + "\n" + command_string) - try: - daq_lib.set_field("program_state", "Program Busy") - execute_command(command_string) - except NameError as e: - error_string = "Unknown command in queue: %s Error: %s" % (command_string, e) - logger.error(error_string) - exc_type, exc_value, exc_traceback = sys.exc_info() - print("*** print_tb:") - traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) - except SyntaxError: - logger.exception("Syntax error") - except KeyError as e: - logger.exception("Key error. Error: %s. Command was: %s" % (e, command_string)) - except TypeError as e: - logger.exception("Type error. Error: %s" % e) - except AttributeError as e: - logger.error("Attribute Error: %s" % e) - except KeyboardInterrupt: - abort_data_collection() - logger.info("Interrupt caught by daq server\n") - if ( - command_string != "pause_data_collection()" - and command_string != "continue_data_collection()" - and command_string != "abort_data_collection()" - and command_string != "unmount_after_abort()" - and command_string != "no_unmount_after_abort()" - ): - daq_lib.set_field("program_state", "Program Ready") + if (command_string == ""): + return + if (command_string == "q"): + sys.exit() + daq_lib.broadcast_output(time.ctime(time.time()) + "\n" + command_string) + try: + daq_lib.set_field("program_state","Program Busy") + execute_command(command_string) + except NameError as e: + error_string = "Unknown command in queue: %s Error: %s" % (command_string, e) + logger.error(error_string) + exc_type, exc_value, exc_traceback = sys.exc_info() + print("*** print_tb:") + traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) + except SyntaxError: + logger.exception("Syntax error") + except KeyError as e: + logger.exception("Key error. Error: %s. Command was: %s" % (e, command_string)) + except TypeError as e: + logger.exception("Type error. Error: %s" % e) + except AttributeError as e: + logger.error("Attribute Error: %s" % e) + except KeyboardInterrupt: + abort_data_collection() + logger.info("Interrupt caught by daq server\n") + if (command_string != "pause_data_collection()" and command_string != "continue_data_collection()" and command_string != "abort_data_collection()" and command_string != "unmount_after_abort()" and command_string != "no_unmount_after_abort()"): + daq_lib.set_field("program_state","Program Ready") def run_server(): - _thread.start_new_thread(process_immediate_commands, (0.25,)) - comm_pv = beamline_support.pvCreate(daq_utils.beamlineComm + "command_s") - beamline_support.pvPut(comm_pv, "\n") - immediate_comm_pv = beamline_support.pvCreate( - daq_utils.beamlineComm + "immediate_command_s" - ) - beamline_support.pvPut(immediate_comm_pv, "\n") - comm_pv.add_callback(comm_cb) - immediate_comm_pv.add_callback(comm2_cb) - process_commands(0.5) + _thread.start_new_thread(process_immediate_commands,(.25,)) + comm_pv = beamline_support.pvCreate(daq_utils.beamlineComm + "command_s") + beamline_support.pvPut(comm_pv,"\n") + immediate_comm_pv = beamline_support.pvCreate(daq_utils.beamlineComm + "immediate_command_s") + beamline_support.pvPut(immediate_comm_pv,"\n") + comm_pv.add_callback(comm_cb) + immediate_comm_pv.add_callback(comm2_cb) + process_commands(0.5) From d7b529b8b9aec44e8e186e8fd5e84cf181e69853 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Tue, 14 Nov 2023 08:09:08 -0500 Subject: [PATCH 70/79] Using send_to_server to directly send messages --- gui/control_main.py | 156 +++++++++++++++------------------- gui/dialog/screen_defaults.py | 4 +- gui/dialog/staff_screen.py | 35 +++----- gui/dialog/user_screen.py | 22 +++-- utils/comm.py | 18 ---- 5 files changed, 92 insertions(+), 143 deletions(-) delete mode 100644 utils/comm.py diff --git a/gui/control_main.py b/gui/control_main.py index 7ed62141..3b9c9dd6 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -52,7 +52,8 @@ from gui.raster import RasterCell, RasterGroup from QPeriodicTable import QPeriodicTable from threads import RaddoseThread, ServerCheckThread, VideoThread -from utils.comm import generate_server_message +import json +from typing import Dict, List, Optional logger = logging.getLogger() @@ -1426,9 +1427,7 @@ def annealButtonCB(self): try: ftime = float(self.annealTime_ledit.text()) if ftime >= 0.1 and ftime <= 5.0: - comm_s = generate_server_message("anneal", [ftime]) - logger.info(comm_s) - self.send_to_server(comm_s) + self.send_to_server("anneal", [ftime]) else: self.popupServerMessage( "Anneal time must be between 0.1 and 5.0 seconds." @@ -2498,9 +2497,7 @@ def protoRadioToggledCB(self, text): pass def beamsizeComboActivatedCB(self, text): - comm_s = generate_server_message("set_beamsize", [text[0:2], text[2:4]]) - logger.info(comm_s) - self.send_to_server(comm_s) + self.send_to_server("set_beamsize", [text[0:2], text[2:4]]) def protoComboActivatedCB(self, text): self.showProtParams() @@ -2605,12 +2602,7 @@ def popImportDialogCB(self): ) self.timerSample.start(SAMPLE_TIMER_DELAY) if fname != "": - logger.info(fname) - comm_s = generate_server_message( - "importSpreadsheet", [fname[0], daq_utils.owner] - ) - logger.info(comm_s) - self.send_to_server(comm_s) + self.send_to_server("importSpreadsheet", [fname[0], daq_utils.owner]) def setUserModeCB(self): self.vidActionDefineCenterRadio.setEnabled(False) @@ -2693,12 +2685,10 @@ def dewarViewToggleCheckCB(self): self.dewarTree.refreshTreePriorityView() def moveOmegaCB(self): - comm_s = generate_server_message( + self.send_to_server( "mvaDescriptor", ["omega", float(self.sampleOmegaMoveLedit.getEntry().text())], ) - logger.info(comm_s) - self.send_to_server(comm_s) def moveEnergyCB(self): energyRequest = float(str(self.energy_ledit.text())) @@ -2706,11 +2696,7 @@ def moveEnergyCB(self): self.popupServerMessage("Energy change must be less than 10 ev") return else: - comm_s = generate_server_message( - "mvaDescriptor", ["energy", float(self.energy_ledit.text())] - ) - logger.info(comm_s) - self.send_to_server(comm_s) + self.send_to_server("mvaDescriptor", ["energy", float(self.energy_ledit.text())]) def setLifetimeCB(self, lifetime): if hasattr(self, "sampleLifetimeReadback_ledit"): @@ -2777,26 +2763,18 @@ def setTransCB(self): except ValueError as e: self.popupServerMessage("Please enter a valid number") return - comm_s = generate_server_message( - "setTrans", ["energy", float(self.transmission_ledit.text())] - ) - logger.info(comm_s) - self.send_to_server(comm_s) + self.send_to_server("setTrans", ["energy", float(self.transmission_ledit.text())]) def setDCStartCB(self): currentPos = float(self.sampleOmegaRBVLedit.getEntry().text()) % 360.0 self.setGuiValues({"osc_start": currentPos}) def moveDetDistCB(self): - comm_s = generate_server_message( - "mvaDescriptor", + self.send_to_server("mvaDescriptor", [ "detectorDist", float(self.detDistMotorEntry.getEntry().text()), - ], - ) - logger.info(comm_s) - self.send_to_server(comm_s) + ]) def omegaTweakNegCB(self): tv = float(self.omegaTweakVal_ledit.text()) @@ -2852,20 +2830,14 @@ def omegaTweakCB(self, tv): self.popupServerMessage("You don't have control") def autoCenterLoopCB(self): - logger.info("auto center loop") - comm_s = generate_server_message("loop_center_xrec") - self.send_to_server(comm_s) + self.send_to_server("loop_center_xrec") def autoRasterLoopCB(self): self.selectedSampleID = self.selectedSampleRequest["sample"] - comm_s = generate_server_message("autoRasterLoop", [self.selectedSampleID]) - self.send_to_server(comm_s) + self.send_to_server("autoRasterLoop", [self.selectedSampleID]) def runRastersCB(self): - comm_s = generate_server_message( - "snakeRaster", [self.selectedSampleRequest["uid"]] - ) - self.send_to_server(comm_s) + self.send_to_server("snakeRaster", [self.selectedSampleRequest["uid"]]) def drawInteractiveRasterCB(self): # any polygon for now, interactive or from xrec for i in range(len(self.polyPointItems)): @@ -2941,8 +2913,7 @@ def center3LoopCB(self): logger.info("3-click center loop") self.threeClickCount = 1 self.click3Button.setStyleSheet("background-color: yellow") - comm_s = generate_server_message("mvaDescriptor", ["omega", 0]) - self.send_to_server(comm_s) + self.send_to_server("mvaDescriptor", ["omega", 0]) def fillPolyRaster( self, rasterReq, waitTime=1 @@ -3119,10 +3090,7 @@ def takeRasterSnapshot(self, rasterReq): reqID=rasterReq["uid"], rasterHeatJpeg=jpegImageFilename, ) - comm_s = generate_server_message( - "ispybLib.insertRasterResult", [str(rasterReq["uid"]), str(visitName)] - ) - self.send_to_server(comm_s) + self.send_to_server("ispybLib.insertRasterResult", [str(rasterReq["uid"]), str(visitName)]) def reFillPolyRaster(self): rasterEvalOption = str(self.rasterEvalComboBox.currentText()) @@ -3221,12 +3189,10 @@ def selectAllCenterCB(self): self.centeringMarksList[i]["graphicsItem"].setSelected(True) def lightUpCB(self): - comm_s = generate_server_message("backlightBrighter") - self.send_to_server(comm_s) + self.send_to_server("backlightBrighter") def lightDimCB(self): - comm_s = generate_server_message("backlightDimmer") - self.send_to_server(comm_s) + self.send_to_server("backlightDimmer") def eraseRastersCB(self): if self.rasterList != []: @@ -3582,22 +3548,21 @@ def pixelSelect(self, event): True ) # because it's easy to forget defineCenter is on if self.zoom4Radio.isChecked(): - comm_s = generate_server_message( + self.send_to_server( "changeImageCenterHighMag", [x_click, y_click, 1] ) elif self.zoom3Radio.isChecked(): - comm_s = generate_server_message( + self.send_to_server( "changeImageCenterHighMag", [x_click, y_click, 0] ) if self.zoom2Radio.isChecked(): - comm_s = generate_server_message( + self.send_to_server( "changeImageCenterLowMag", [x_click, y_click, 1] ) elif self.zoom1Radio.isChecked(): - comm_s = generate_server_message( + self.send_to_server( "changeImageCenterLowMag", [x_click, y_click, 0] ) - self.send_to_server(comm_s) return if self.vidActionRasterDefRadio.isChecked(): self.click_positions.append(event.pos()) @@ -3625,7 +3590,7 @@ def pixelSelect(self, event): if self.threeClickCount > 0: # 3-click centering self.threeClickCount = self.threeClickCount + 1 - comm_s = generate_server_message( + comm_s = self.generate_server_message( "center_on_click", [ correctedC2C_x, @@ -3636,7 +3601,7 @@ def pixelSelect(self, event): ], ) else: - comm_s = generate_server_message( + comm_s = self.generate_server_message( "center_on_click", [ correctedC2C_x, @@ -4206,24 +4171,24 @@ def collectQueueCB(self): if currentRequest == {}: self.addRequestsToAllSelectedCB() logger.info("running queue") - comm_s = generate_server_message("runDCQueue") - self.send_to_server(comm_s) + self.send_to_server("runDCQueue") + def warmupGripperCB(self): - comm_s = generate_server_message("warmupGripper") - self.send_to_server(comm_s) + self.send_to_server("warmupGripper") + def dryGripperCB(self): - comm_s = generate_server_message("dryGripper") - self.send_to_server(comm_s) + self.send_to_server("dryGripper") + def enableTScreenGripperCB(self): - comm_s = generate_server_message("enableDewarTscreen") - self.send_to_server(comm_s) + self.send_to_server("enableDewarTscreen") + def parkGripperCB(self): - comm_s = generate_server_message("parkGripper") - self.send_to_server(comm_s) + self.send_to_server("parkGripper") + def restartServerCB(self): if self.controlEnabled(): @@ -4454,17 +4419,15 @@ def puckToDewarCB(self): def stopRunCB(self): logger.info("stopping collection") - comm_s = generate_server_message("stopDCQueue", [1]) - self.aux_send_to_server(comm_s) + self.aux_send_to_server("stopDCQueue", [1]) def stopQueueCB(self): logger.info("stopping queue") if self.pauseQueueButton.text() == "Continue": - comm_s = generate_server_message("continue_data_collection") - self.aux_send_to_server(comm_s) + self.aux_send_to_server("continue_data_collection") else: - comm_s = generate_server_message("stopDCQueue", [2]) - self.aux_send_to_server(comm_s) + self.aux_send_to_server("stopDCQueue", [2]) + def mountSampleCB(self): if getBlConfig("mountEnabled") == 0: @@ -4479,8 +4442,8 @@ def mountSampleCB(self): else: # No sample ID found, do nothing logger.info("No sample selected, cannot mount") return - comm_s = generate_server_message("mountSample", [self.selectedSampleID]) - self.send_to_server(comm_s) + self.send_to_server("mountSample", [self.selectedSampleID]) + self.zoom2Radio.setChecked(True) self.zoomLevelToggledCB("Zoom2") self.protoComboBox.setCurrentIndex(self.protoComboBox.findText(str("standard"))) @@ -4488,8 +4451,8 @@ def mountSampleCB(self): def unmountSampleCB(self): logger.info("unmount sample") - comm_s = generate_server_message("unmountSample") - self.send_to_server(comm_s) + self.send_to_server("unmountSample") + def refreshCollectionParams(self, selectedSampleRequest, validate_hdf5=True): reqObj = selectedSampleRequest["request_obj"] @@ -4605,14 +4568,14 @@ def refreshCollectionParams(self, selectedSampleRequest, validate_hdf5=True): > 5.0 ): - comm_s = generate_server_message( + self.send_to_server( "mvaDescriptor", [ "omega", selectedSampleRequest["request_obj"]["rasterDef"]["omega"], ], ) - self.send_to_server(comm_s) + if str(reqObj["protocol"]) == "eScan": try: self.escan_steps_ledit.setText(str(reqObj["steps"])) @@ -5202,24 +5165,41 @@ def controlEnabled(self): and self.controlMasterCheckBox.isChecked() ) - def send_to_server(self, s): - if s == "lockControl": + def send_to_server(self, function_name: str, args: "Optional[List]" = None, kwargs: "Optional[Dict]" = None): + if function_name == "lockControl": self.controlMaster_pv.put(0 - self.processID) return - if s == "unlockControl": + if function_name == "unlockControl": self.controlMaster_pv.put(self.processID) return if self.controlEnabled(): time.sleep(0.01) - logger.info("send_to_server: %s" % s) - self.comm_pv.put(s) + message = self.generate_server_message(function_name, args, kwargs) + logger.info(f"send_to_server: {message}") + self.comm_pv.put(message) else: self.popupServerMessage("You don't have control") - def aux_send_to_server(self, s): + def generate_server_message( + self, function_name: str, args: "Optional[List]" = None, kwargs: "Optional[Dict]" = None + ) -> str: + if not args: + args = [] + if not kwargs: + kwargs = {} + return json.dumps( + { + "function": function_name, + "args": args, + "kwargs": kwargs, + } + ) + + def aux_send_to_server(self, function_name: str, args: "Optional[List]" = None, kwargs: "Optional[Dict]" = None): if self.controlEnabled(): time.sleep(0.01) - logger.info("aux_send_to_server: %s" % s) - self.immediate_comm_pv.put(s) + message = self.generate_server_message(function_name, args, kwargs) + logger.info(f"aux_send_to_server: {message}") + self.immediate_comm_pv.put(message) else: self.popupServerMessage("You don't have control") diff --git a/gui/dialog/screen_defaults.py b/gui/dialog/screen_defaults.py index 7dcc743a..6a86e881 100644 --- a/gui/dialog/screen_defaults.py +++ b/gui/dialog/screen_defaults.py @@ -14,7 +14,6 @@ VALID_EXP_TIMES, ) from daq_utils import beamline, getBlConfig, setBlConfig -from utils.comm import generate_server_message if typing.TYPE_CHECKING: from lsdcGui import ControlMain @@ -203,8 +202,7 @@ def reprocessRasterRequestCB(self): try: reqID = self.parent.selectedSampleRequest["uid"] self.parent.drawPolyRaster(db_lib.getRequestByID(reqID)) - comm_s = generate_server_message("reprocessRaster", [reqID]) - self.parent.send_to_server(comm_s) + self.parent.send_to_server("reprocessRaster", [reqID]) except: pass diff --git a/gui/dialog/staff_screen.py b/gui/dialog/staff_screen.py index 07a5f7d5..13ce99d1 100644 --- a/gui/dialog/staff_screen.py +++ b/gui/dialog/staff_screen.py @@ -6,7 +6,6 @@ from config_params import BEAM_CHECK, TOP_VIEW_CHECK, UNMOUNT_COLD_CHECK from daq_utils import getBlConfig, setBlConfig -from utils.comm import generate_server_message if typing.TYPE_CHECKING: from lsdcGui import ControlMain @@ -223,15 +222,13 @@ def getFastDPNodeList(self): return nodeList def setFastDPNodesCB(self): - comm_s = generate_server_message( + self.parent.send_to_server( "fastDPNodes", [ int(self.fastDPNodeEntryList[i].text()) for i in range(self.fastDPNodeCount) ], ) - logger.info(comm_s) - self.parent.send_to_server(comm_s) def lockGuiCB(self): self.parent.send_to_server("lockControl") @@ -240,52 +237,46 @@ def unLockGuiCB(self): self.parent.send_to_server("unlockControl") def setSpotNodesCB(self): - comm_s = generate_server_message( + self.parent.send_to_server( "spotNodes", [int(self.spotNodeEntryList[i].text()) for i in range(self.spotNodeCount)], ) - logger.info(comm_s) - self.parent.send_to_server(comm_s) def unmountColdCB(self): - comm_s = generate_server_message("unmountCold") - self.parent.send_to_server(comm_s) + self.parent.send_to_server("unmountCold") def openPort1CB(self): - comm_s = generate_server_message("openPort", [1]) - self.parent.send_to_server(comm_s) + self.parent.send_to_server("openPort", [1]) def setBeamcenterCB(self): - comm_s = generate_server_message( + self.parent.send_to_server( "set_beamcenter", [self.beamcenterX_ledit.text(), self.beamcenterY_ledit.text()], ) - self.parent.send_to_server(comm_s) - def closePortsCB(self): - self.parent.send_to_server(generate_server_message("closePorts")) + self.parent.send_to_server("closePorts") def clearMountedSampleCB(self): - self.parent.send_to_server(generate_server_message("clearMountedSample")) + self.parent.send_to_server("clearMountedSample") def recoverRobotCB(self): - self.parent.aux_send_to_server(generate_server_message("recoverRobot")) + self.parent.aux_send_to_server("recoverRobot") def rebootEMBL_CB(self): - self.parent.aux_send_to_server(generate_server_message("rebootEMBL")) + self.parent.aux_send_to_server("rebootEMBL") def restartEMBL_CB(self): - self.parent.send_to_server(generate_server_message("restartEMBL")) + self.parent.send_to_server("restartEMBL") def openGripper_CB(self): - self.parent.send_to_server(generate_server_message("openGripper")) + self.parent.send_to_server("openGripper") def closeGripper_CB(self): - self.parent.send_to_server(generate_server_message("closeGripper")) + self.parent.send_to_server("closeGripper") def homePinsCB(self): - self.parent.send_to_server(generate_server_message("homePins")) + self.parent.send_to_server("homePins") def robotOnCheckCB(self, state): if state == QtCore.Qt.Checked: diff --git a/gui/dialog/user_screen.py b/gui/dialog/user_screen.py index f8b9b5cf..7b6137c7 100644 --- a/gui/dialog/user_screen.py +++ b/gui/dialog/user_screen.py @@ -198,24 +198,22 @@ def __init__(self, parent: "ControlMain"): self.setLayout(vBoxColParams1) def setSlit1XCB(self): - comm_s = generate_server_message("setSlit1X", [self.slit1XMotor_ledit.text()]) - self.parent.send_to_server(comm_s) + self.parent.send_to_server("setSlit1X", [self.slit1XMotor_ledit.text()]) def setSlit1YCB(self): - comm_s = generate_server_message("setSlit1Y", [self.slit1YMotor_ledit.text()]) - self.parent.send_to_server(comm_s) + self.parent.send_to_server("setSlit1Y", [self.slit1YMotor_ledit.text()]) def unmountColdCB(self): - self.parent.send_to_server(generate_server_message("unmountCold")) + self.parent.send_to_server("unmountCold") def testRobotCB(self): - self.parent.send_to_server(generate_server_message("testRobot")) + self.parent.send_to_server("testRobot") def recoverRobotCB(self): - self.parent.send_to_server(generate_server_message("recoverRobot")) + self.parent.send_to_server("recoverRobot") def dryGripperCB(self): - self.parent.send_to_server(generate_server_message("dryGripper")) + self.parent.send_to_server("dryGripper") def stopDetCB(self): logger.info("stopping detector") @@ -236,16 +234,16 @@ def rebootZebraIOC_CB(self): self.parent.rebootZebraIOC_pv.put(1) def SEgovCB(self): - self.parent.send_to_server(generate_server_message("setGovState", ["SE"])) + self.parent.send_to_server("setGovState", ["SE"]) def SAgovCB(self): - self.parent.send_to_server(generate_server_message("setGovState", ["SA"])) + self.parent.send_to_server("setGovState", ["SA"]) def DAgovCB(self): - self.parent.send_to_server(generate_server_message("setGovState", ["DA"])) + self.parent.send_to_server("setGovState", ["DA"]) def BLgovCB(self): - self.parent.send_to_server(generate_server_message("setGovState", ["BL"])) + self.parent.send_to_server("setGovState", ["BL"]) def userScreenOKCB(self): self.hide() diff --git a/utils/comm.py b/utils/comm.py deleted file mode 100644 index 0cc41898..00000000 --- a/utils/comm.py +++ /dev/null @@ -1,18 +0,0 @@ -import json -from typing import Dict, List, Optional - - -def generate_server_message( - function_name: str, args: Optional[List] = None, kwargs: Optional[Dict] = None -) -> str: - if not args: - args = [] - if not kwargs: - kwargs = {} - return json.dumps( - { - "function": function_name, - "args": args, - "kwargs": kwargs, - } - ) From 472310bc5485526f936f3b176e0f8f765402d003 Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Tue, 14 Nov 2023 11:19:18 -0500 Subject: [PATCH 71/79] Removed missing import --- gui/dialog/user_screen.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gui/dialog/user_screen.py b/gui/dialog/user_screen.py index 7b6137c7..7322efcc 100644 --- a/gui/dialog/user_screen.py +++ b/gui/dialog/user_screen.py @@ -6,7 +6,6 @@ from qtpy.QtWidgets import QCheckBox import daq_utils -from utils.comm import generate_server_message if typing.TYPE_CHECKING: from lsdcGui import ControlMain From 79071fec2b4b13dd2640ddd57b3ac34f52bcaae7 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Tue, 14 Nov 2023 13:44:25 -0500 Subject: [PATCH 72/79] Better exception handling --- daq_main_common.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/daq_main_common.py b/daq_main_common.py index bdf8b303..77fe3115 100755 --- a/daq_main_common.py +++ b/daq_main_common.py @@ -86,9 +86,13 @@ def execute_command(command_s): try: command: "dict[str, Any]" = json.loads(command_s) func = whitelisted_functions[command["function"]] + except Exception as e: + logger.exception(f"Error in function parsing and lookup: {e}") + + try: func(*command["args"], **command["kwargs"]) except Exception as e: - logger.error(f"Error executing {command_s}: {e}") + logger.exception(f"Error executing {command_s}: {e}") def pybass_init(): From 6a35029f6ce1993ae456b8027d59d5c62bf64f86 Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra <133771854+NYSBC-Rudra@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:25:21 -0500 Subject: [PATCH 73/79] making changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit still need to Make new container by copying current container db_lib.getContainerByName(db_lib.primaryDewarName, 'nyx') Update the [‘contents’] of container to make it length of 24 Then use db_lib.updateContainer(new container) --- config_params.py | 2 +- gui/dialog/dewar.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config_params.py b/config_params.py index 49198de9..0b5e87c9 100644 --- a/config_params.py +++ b/config_params.py @@ -76,7 +76,7 @@ class RasterStatus(Enum): GOVERNOR_TIMEOUT = 120 # seconds for a governor move -DEWAR_SECTORS = {'amx':8, 'fmx':8, 'nyx':5} +DEWAR_SECTORS = {'amx':8, 'fmx':8, 'nyx':8} PUCKS_PER_DEWAR_SECTOR = {'amx':3, 'fmx':3, 'nyx':3} cryostreamTempPV = {'amx': 'AMX:cs700:gasT-I', 'fmx': 'FMX:cs700:gasT-I'} diff --git a/gui/dialog/dewar.py b/gui/dialog/dewar.py index b3ae55cd..016cb4a5 100644 --- a/gui/dialog/dewar.py +++ b/gui/dialog/dewar.py @@ -59,7 +59,7 @@ def initUI(self): for j in range(0, self.pucksPerDewarSector): dataIndex = (i * self.pucksPerDewarSector) + j self.allButtonList[dataIndex] = QtWidgets.QPushButton( - (str(self.data[dataIndex])) + '{}: {}'.format(str(dataIndex+1),str(self.data[dataIndex])) ) self.allButtonList[dataIndex].clicked.connect( functools.partial(self.on_button, str(dataIndex)) From 7a4e958c6b42c4d316a148e862282549298d3683 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Tue, 14 Nov 2023 17:14:05 -0500 Subject: [PATCH 74/79] Removed unecessary generate_server_message --- gui/control_main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 3b9c9dd6..294c45a3 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -1,10 +1,12 @@ import _thread import functools +import json import logging import math import os import sys import time +from typing import Dict, List, Optional import cv2 import numpy as np @@ -52,8 +54,6 @@ from gui.raster import RasterCell, RasterGroup from QPeriodicTable import QPeriodicTable from threads import RaddoseThread, ServerCheckThread, VideoThread -import json -from typing import Dict, List, Optional logger = logging.getLogger() @@ -3590,7 +3590,7 @@ def pixelSelect(self, event): if self.threeClickCount > 0: # 3-click centering self.threeClickCount = self.threeClickCount + 1 - comm_s = self.generate_server_message( + comm_s = ( "center_on_click", [ correctedC2C_x, @@ -3601,7 +3601,7 @@ def pixelSelect(self, event): ], ) else: - comm_s = self.generate_server_message( + comm_s = ( "center_on_click", [ correctedC2C_x, @@ -3612,7 +3612,7 @@ def pixelSelect(self, event): ], ) if not self.vidActionRasterExploreRadio.isChecked(): - self.aux_send_to_server(comm_s) + self.aux_send_to_server(*comm_s) if self.threeClickCount == 4: self.threeClickCount = 0 self.click3Button.setStyleSheet("background-color: None") From 21acc7f542c75218c169adf7f88ff41e90b2bb91 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Wed, 15 Nov 2023 09:15:58 -0500 Subject: [PATCH 75/79] Fixed bug where setTrans was passing an extra arg --- gui/control_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/control_main.py b/gui/control_main.py index 294c45a3..03390db8 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -2763,7 +2763,7 @@ def setTransCB(self): except ValueError as e: self.popupServerMessage("Please enter a valid number") return - self.send_to_server("setTrans", ["energy", float(self.transmission_ledit.text())]) + self.send_to_server("setTrans", [float(self.transmission_ledit.text())]) def setDCStartCB(self): currentPos = float(self.sampleOmegaRBVLedit.getEntry().text()) % 360.0 From 93e8107ff22e1476608927b416b482126531b147 Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra <133771854+NYSBC-Rudra@users.noreply.github.com> Date: Mon, 20 Nov 2023 19:24:03 -0500 Subject: [PATCH 76/79] updating error --- gui/dialog/resolution_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/dialog/resolution_dialog.py b/gui/dialog/resolution_dialog.py index 88c6c8d8..f1fab0af 100644 --- a/gui/dialog/resolution_dialog.py +++ b/gui/dialog/resolution_dialog.py @@ -179,7 +179,7 @@ def calculateValue(self): elif checked_key == 'L': - D_value = float(self.D_value_enter.displayText()) + d_value = float(self.d_value_enter.displayText()) theta_value = float(self.theta_value_enter.displayText()) wave_value = float(self.wave_value_enter.displayText()) From 3ad95f483f671deaa8f2249993f9c5e22cbc7cc8 Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra <133771854+NYSBC-Rudra@users.noreply.github.com> Date: Mon, 20 Nov 2023 19:39:09 -0500 Subject: [PATCH 77/79] adding devices --- devices.py | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 devices.py diff --git a/devices.py b/devices.py new file mode 100644 index 00000000..32ad69c1 --- /dev/null +++ b/devices.py @@ -0,0 +1,310 @@ +from ophyd import Device, Component as Cpt, EpicsSignal, EpicsSignalRO, PVPositioner +import socket +from ophyd.status import SubscriptionStatus +import os + +class MD2Positioner(PVPositioner): + setpoint = Cpt(EpicsSignal, 'Position', name='setpoint') + readback = Cpt(EpicsSignal, 'Position', name='readback') + state = Cpt(EpicsSignalRO, 'State', name='state') + done = Cpt(EpicsSignalRO, 'State', name='done') + precision = Cpt(EpicsSignalRO, 'Precision', name='precision') + done_value = 4 # MD2 Enum, 4 = Ready + # TODO: Add limits, settle_time, timeout or defaults for each + + def val(self): + return self.get().readback + +class ExporterComponent(Cpt): + def __init__(self, address, port, name, **kwargs): + super().__init__(self, **kwargs) + self.name = name + self.address = address + self.port = port + self.sock = None # socket.socket(socket.AF_INET, socket.SOCK_STREAM) + #self.sock.settimeout(5) + + def get(self): + return () + + def connect(self): + # for establishing connection, using context manager is preferred + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((self.address, self.port)) + + def disconnect(self): + self.sock.close() + self.sock = None + + def send_data(self, data): + STX = chr(2) + ETX = chr(3) + data = STX + data + ETX + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((self.address, self.port)) + s.sendto(data.encode(), (self.address, self.port)) + state = s.recv(4096) + print(f'state: {self.decipher_reply(state)}') + ret = None + while ret == None: + output = s.recv(4096) + output = self.decipher_reply(output) + ret = self.process_ret(output) + return ret + + def read_stream(self): + output = None + while output == None: + output, addr = self.sock.recvfrom(4096) + print(f'output:{self.decipher_reply(output)}') + output = None + + def write(self, attribute, value): + return self.send_data('WRTE ' + attribute + ' ' + str(value)) + + def read(self, attribute): + return (self.send_data('READ ' + attribute + ' ')).split(" ")[0][4:] + + def cmd(self, method, parameters): + parameters = map(str, parameters) + params = "\t".join(parameters) + return self.send_data('EXEC ' + method + ' ' + str(params)) + + def decipher_reply(self, reply): + # Specifically for decoding MD2 Exporter replies + reply = str(reply) + reply = reply.replace("\\x03", "") + reply = reply.replace("\\x1f", ", ") + reply = reply.replace("\\x02", "\n") + reply = reply.replace("\\t", " ") + return reply[2:-1] + + def process_ret(self, ret): + # Returns only when an error or return value is found, + # ignores events to ensure flyer kickoff functions as expected + for line in ret.split("\n"): + if "ERR:" in line: + print(f"error: {line}") + return line + elif "RET:" in line: + print(f"returned: {line}") + return line + elif "NULL" in line: + print(f"null error: {line}") + return line + elif "EVT:" in line: # print here if you want to see the events + pass #return self.process_evt(line) + +class LightDevice(Device): + control = Cpt(EpicsSignal, 'LightIsOn', name='control') + factor = Cpt(EpicsSignal, 'LightFactor', name='factor') + level = Cpt(EpicsSignal, 'LightLevel', name='level') + + def is_on(self): + return self.control.get() == 1 + + def turn_on(self): + self.control.set(1) + + def turn_off(self): + self.control.set(0) + + def set_factor(self, factor): + self.factor.set(factor) + + def set_level(self, level): + self.level.set(level) + +class BeamstopDevice(Device): + distance = Cpt(MD2Positioner, "BeamstopDistance", name="distance") + x = Cpt(MD2Positioner, "BeamstopX", name="x") + y = Cpt(MD2Positioner, "BeamstopY", name="y") + z = Cpt(MD2Positioner, "BeamstopZ", name="z") + pos = Cpt(EpicsSignal, "BeamstopPosition", name="pos") + +class MD2SimpleHVDevice(Device): + horizontal = Cpt(MD2Positioner, "HVHorizontal", name="horizontal") + vertical = Cpt(MD2Positioner, "HVVertical", name="vertical") + pos = Cpt(EpicsSignal, "HVPosition", name="pos") + # Current aperture/scintillator/capillary predifined position. + # Enum: the aperture position: + # 0: PARK, under cover. + # 1: BEAM, selected aperture aligned with beam. + # 2: OFF, just below the OAV. + # 3: UNKNOWN, not in a predefined position (this cannot be set). + +class GonioDevice(Device): + omega = Cpt(MD2Positioner, 'Omega',name='omega') + x = Cpt(MD2Positioner, 'AlignmentX',name='x') + y = Cpt(MD2Positioner, 'AlignmentY',name='y') + z = Cpt(MD2Positioner, 'AlignmentZ',name='z') + cx = Cpt(MD2Positioner, 'CentringX',name='cx') + cy = Cpt(MD2Positioner, 'CentringY',name='cy') + +class MD2Device(GonioDevice): + # TODO: Enum for MD2 phases and states + cx = Cpt(MD2Positioner, 'CentringX',name='cx') + cy = Cpt(MD2Positioner, 'CentringY',name='cy') + center_pixel_x = Cpt(EpicsSignalRO, 'BeamPositionHorizontal',name='center_pixel_x') + center_pixel_y = Cpt(EpicsSignalRO, 'BeamPositionVertical',name='center_pixel_y') + centring_click = Cpt(EpicsSignal, 'setCentringClick',name='centring_click') + state = Cpt(EpicsSignalRO, 'State',name='state') + phase = Cpt(EpicsSignal, 'CurrentPhase',name='phase') + phase_index = Cpt(EpicsSignalRO, 'CurrentPhaseIndex',name='phase_index') + detector_state = Cpt(EpicsSignal, 'DetectorState',name='det_state') + detector_gate_pulse_enabled = Cpt(EpicsSignal, 'DetectorGatePulseEnabled',name='det_gate_pulse_enabled') + exporter = Cpt(ExporterComponent, address=os.environ['EXPORTER_HOST'], port=int(os.environ['EXPORTER_PORT']), name='exporter') + + def is_ready(self): + return self.state.get() == 4 + + def ready_status(self): + # returns an ophyd status object that monitors the state pv for operations to complete + def check_ready(*, old_value, value, **kwargs): + "Return True when the MD2 is ready" + return (value == 4) + return SubscriptionStatus(self.state, check_ready) + + def phase_transition(self, phase): + # returns an ophyd status object that monitors the phase pv for operations to complete + self.phase.put(phase) + def check_transition_state(*, old_value, value, **kwargs): + "Return True when the MD2 is ready" + return (value == 4 and self.phase.get() == phase) + return SubscriptionStatus(self.state, check_transition_state) + + def standard_scan(self, + frame_number=0, # int: frame ID just for logging purposes. + num_images=1, # int: number of frames. Needed solely when the detector use gate enabled trigger. + start_angle=0, # double: angle (deg) at which the shutter opens and omega speed is stable. + scan_range=0.1, # double: omega relative move angle (deg) before closing the shutter. + exposure_time=0.1, # double: exposure time (sec) to control shutter command. + num_passes=1 # int: number of moves forward and reverse between start angle and stop angle + ): + command = 'startScanEx2' + if start_angle is None: + start_angle=self.omega.get() + return self.exporter.cmd(command, [frame_number, num_images, start_angle, scan_range, exposure_time, num_passes]) + + def vector_scan(self, + start_angle=None, # double: angle (deg) at which the shutter opens and omega speed is stable. + scan_range=10, # double: omega relative move angle (deg) before closing the shutter. + exposure_time=1, # double: exposure time (sec) to control shutter command. + start_y=None, # double: PhiY axis position at the beginning of the exposure. + start_z=None, # double: PhiZ axis position at the beginning of the exposure. + start_cx=None, # double: CentringX axis position at the beginning of the exposure. + start_cy=None, # double: CentringY axis position at the beginning of the exposure. + stop_y=None, # double: PhiY axis position at the end of the exposure. + stop_z=None, # double: PhiZ axis position at the end of the exposure. + stop_cx=None, # double: CentringX axis position at the end of the exposure. + stop_cy=None, # double: CentringY axis position at the end of the exposure. + ): + command = 'startScan4DEx' + if start_angle is None: + start_angle = self.omega.val() + if start_y is None: + start_y = self.y.val() + if start_z is None: + start_z = self.z.val() + if start_cx is None: + start_cx = self.cx.val() + if start_cy is None: + start_cy = self.cy.val() + if stop_y is None: + stop_y = self.y.val() + if stop_z is None: + stop_z = self.z.val() + if stop_cx is None: + stop_cx = self.cx.val() + if stop_cy is None: + stop_cy = self.cy.val() + + # List of scan parameters values, comma separated. The axes start values define the beginning + # of the exposure, that is when all the axes have a steady speed and when the shutter/detector + # are triggered. + # The axes stop values are for the end of detector exposure and define the position at the + # beginning of the deceleration. + # Inputs names: "start_angle", "scan_range", "exposure_time", "start_y", "start_z", "start_cx", + # "start_cy", "stop_y", "stop_z", "stop_cx", "stop_cy" + param_list = [start_angle, scan_range, exposure_time, + start_y, start_z, start_cx, start_cy, + stop_y, stop_z, stop_cx, stop_cy] + return self.exporter.cmd(command, param_list) + + def raster_scan(self, + omega_range=0, # double: omega relative move angle (deg) before closing the shutter. + line_range=0.1, # double: horizontal range of the grid (mm). + total_uturn_range=0.1, # double: vertical range of the grid (mm). + start_omega=None, # double: angle (deg) at which the shutter opens and omega speed is stable. + start_y=None, # double: PhiY axis position at the beginning of the exposure. + start_z=None, # double: PhiZ axis position at the beginning of the exposure. + start_cx=None, # double: CentringX axis position at the beginning of the exposure. + start_cy=None, # double: CentringY axis position at the beginning of the exposure. + number_of_lines=5, # int: number of frames on the vertical range. + frames_per_line=5, # int: number of frames on the horizontal range. + exposure_time=1.2, # double: exposure time (sec) to control shutter command. +1, based on the exaples given + invert_direction=True, # boolean: true to enable passes in the reverse direction. + use_centring_table=True, # boolean: true to use the centring table to do the pitch movements. + use_fast_mesh_scans=True # boolean: true to use the fast raster scan if available (power PMAC). + ): + + command = 'startRasterScanEx' + if start_omega is None: + start_omega = self.omega.val() + if start_y is None: + start_y = self.y.val() + if start_z is None: + start_z = self.z.val() + if start_cx is None: + start_cx = self.cx.val() + if start_cy is None: + start_cy = self.cy.val() + # List of scan parameters values, "/t" separated. The axes start values define the beginning + # of the exposure, that is when all the axes have a steady speed and when the shutter/detector + # are triggered. + # The axes stop values are for the end of detector exposure and define the position at the + # beginning of the deceleration. + # Inputs names: "omega_range", "line_range", "total_uturn_range", "start_omega", "start_y", + # "start_z", "start_cx", "start_cy", "number_of_lines", "frames_per_lines", "exposure_time", + # "invert_direction", "use_centring_table", "use_fast_mesh_scans" + param_list = [omega_range, line_range, total_uturn_range, start_omega, start_y, start_z, + start_cx, start_cy, number_of_lines, frames_per_line, exposure_time, + invert_direction, use_centring_table, use_fast_mesh_scans] + return self.exporter.cmd(command, param_list) + +class MD2ApertureDevice(Device): + # this device needs additional signals for "CurrentApertureDiameterIndex" and "ApertureDiameters" + current_index = Cpt(EpicsSignal, 'CurrentApertureDiameterIndex', name='current_index') + diameters = Cpt(EpicsSignalRO, 'ApertureDiameters', name='diameters') + + def get_diameter_list(self): + # must format list for GUI + # also, can't currently get from PV + return self.diameters.get() + + def set_diameter(self, diameter): + if diameter in self.get_diameter_list(): + self.current_index.put(self.get_diameter_list().index(diameter)) + +class ShutterDevice(Device): + control = Cpt(EpicsSignal, '{MD2}:FastShutterIsOpen', name='control') # PV to send control signal + pos_opn = Cpt(EpicsSignalRO, '{Gon:1-Sht}Pos:Opn-I', name='pos_opn') + pos_cls = Cpt(EpicsSignalRO, '{Gon:1-Sht}Pos:Cls-I', name='pos_cls') + + def is_open(self): + return self.control.get() == 1 #self.pos_opn.get() + + def open_shutter(self): + self.control.set(1)#self.pos_opn.get()) iocs are down, so just setting it to 1 + + def close_shutter(self): + self.control.set(0)#self.pos_cls.get()) + +class CameraDevice(Device): + # MD2 camera has the following attributes: + # CoaxCamScaleX + # CoaxCamScaleY + # CoaxialCameraZoomValue + scale_x = Cpt(EpicsSignalRO, 'CoaxCamScaleX', name='scale_x') + scale_y = Cpt(EpicsSignalRO, 'CoaxCamScaleY', name='scale_y') + zoom = Cpt(EpicsSignal, 'CoaxialCameraZoomValue', name='zoom') From b61960da5763aeb0d556d2540bfdd2815a4913ee Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra <133771854+NYSBC-Rudra@users.noreply.github.com> Date: Mon, 4 Dec 2023 10:54:44 -0500 Subject: [PATCH 78/79] fixing dewar --- gui/control_main.py | 42 +- gui/dialog/__init__.py | 3 +- gui/dialog/resolution_calculator.py | 97 --- gui/dialog/resolution_dialog.py | 247 ------- md2_flyers.py | 269 +++++++ setenergy_lsdc.py | 1004 +++++++++++++++++++++++++++ 6 files changed, 1275 insertions(+), 387 deletions(-) delete mode 100644 gui/dialog/resolution_calculator.py delete mode 100644 gui/dialog/resolution_dialog.py create mode 100644 md2_flyers.py create mode 100644 setenergy_lsdc.py diff --git a/gui/control_main.py b/gui/control_main.py index e542f8fe..5d65d1fb 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -50,7 +50,6 @@ SnapCommentDialog, StaffScreenDialog, UserScreenDialog, - CalculatorWindow ) from gui.raster import RasterCell, RasterGroup from QPeriodicTable import QPeriodicTable @@ -271,9 +270,6 @@ def parseXRFTable(self): XRFInfoDict[tokens[0]] = int(float(tokens[5]) * 100) XRFFile.close() return XRFInfoDict - - - def closeEvent(self, evnt): evnt.accept() @@ -2729,10 +2725,6 @@ def popBaseDirectoryDialogCB(self): ) if fname != "": self.dataPathGB.setBasePath_ledit(fname) - ''' - Creating function to for Actions under file drop down - - ''' def popImportDialogCB(self): #self.timerSample.stop() @@ -2755,14 +2747,6 @@ def setUserModeCB(self): def setExpertModeCB(self): self.vidActionDefineCenterRadio.setEnabled(True) - - - def openResolution(self): - self.sub = CalculatorWindow(self) - self.sub.show() - - - def upPriorityCB( self, @@ -5105,26 +5089,6 @@ def initUI(self): splitter1.addWidget(self.tabs) self.setCentralWidget(splitter1) splitterSizes = [600, 100] - - ''' - creating drop down menu items under File - - for now has - importAction - importing spreadsheet manually - @function - popImportDialogCB - - userAction - User Mode - @function - setUserModeCB - - expertAction - Expert Mode - @function - setExpertModeCB - - staffAction - Staff Panel - @function - popStaffDialogCB - - resolutionAction - Resolution Calculator - @function - openResolution - ''' importAction = QtWidgets.QAction("Import Spreadsheet...", self) importAction.triggered.connect(self.popImportDialogCB) modeGroup = QtWidgets.QActionGroup(self) @@ -5136,8 +5100,6 @@ def initUI(self): self.expertAction.triggered.connect(self.setExpertModeCB) self.staffAction = QtWidgets.QAction("Staff Panel...", self) self.staffAction.triggered.connect(self.popStaffDialogCB) - self.resolutionAction = QtWidgets.QAction("Resolution Calculator", self) - self.resolutionAction.triggered.connect(self.openResolution) modeGroup.addAction(self.userAction) modeGroup.addAction(self.expertAction) exitAction = QtWidgets.QAction(QtGui.QIcon("exit24.png"), "Exit", self) @@ -5147,7 +5109,6 @@ def initUI(self): self.statusBar() self.queue_collect_status_widget = QtWidgets.QLabel("Queue Collect: ON") self.statusBar().addPermanentWidget(self.queue_collect_status_widget) - menubar = self.menuBar() fileMenu = menubar.addMenu("&File") settingsMenu = menubar.addMenu("Settings") @@ -5155,7 +5116,6 @@ def initUI(self): fileMenu.addAction(self.userAction) fileMenu.addAction(self.expertAction) fileMenu.addAction(self.staffAction) - fileMenu.addAction(self.resolutionAction) # Define all of the available actions for the overlay color group self.BlueOverlayAction = QtWidgets.QAction("Blue", self, checkable=True) self.RedOverlayAction = QtWidgets.QAction("Red", self, checkable=True) @@ -5448,4 +5408,4 @@ def aux_send_to_server(self, s): logger.info("aux_send_to_server: %s" % s) self.immediate_comm_pv.put(s) else: - self.popupServerMessage("You don't have control") + self.popupServerMessage("You don't have control") \ No newline at end of file diff --git a/gui/dialog/__init__.py b/gui/dialog/__init__.py index 653c2b05..de45f4e8 100644 --- a/gui/dialog/__init__.py +++ b/gui/dialog/__init__.py @@ -5,5 +5,4 @@ from .puck_dialog import PuckDialog from .dewar import DewarDialog from .screen_defaults import ScreenDefaultsDialog -from .resolution_calculator import Calculator -from .resolution_dialog import CalculatorWindow + diff --git a/gui/dialog/resolution_calculator.py b/gui/dialog/resolution_calculator.py deleted file mode 100644 index 394f9ab9..00000000 --- a/gui/dialog/resolution_calculator.py +++ /dev/null @@ -1,97 +0,0 @@ -import math -import sys - - -class Calculator: - """ - Make a calculator object that can calculate resolution formulas (and nothing else) - - """ - def __init__(self): - self.r = None - self.d = None - self.L = None - self.theta = None - self.wavelength = None - - def set_all_variables(self, variable_dict): - for key in variable_dict: - self.set_variables(key, variable_dict[key]) - - - - def set_variables(self, name, value): - if name == 'r': - self.r = value - elif name == 'd': - self.d = value - elif name == 'L': - self.L = value - elif name == 'theta': - self.theta = value - elif name == 'wavelength': - self.wavelength = value - - def calcD(self, r = None, L = None, wavelength = None, theta = None): - r = r or self.r - L = L or self.L - wavelength = wavelength or self.wavelength - theta = theta or self.theta - try: - denominator = 2*(math.sin((0.5*math.atan(r/L)) + theta)) - numerator = wavelength - return numerator/denominator - except Exception as e: - return e - - - - - def calcL(self, r = None, d = None, wavelength = None, theta = None): - r = r or self.r - d = d or self.d - wavelength = wavelength or self.wavelength - theta = theta or self.theta - try: - denominator = math.tan((2* math.asin(wavelength/(2*d) ) ) -(2*theta) ) - numerator = r - return numerator/denominator - except Exception as e: - return e - - - def calcTheta(self, r = None, L = None, wavelength = None, d = None): - r = r or self.r - L = L or self.L - wavelength = wavelength or self.wavelength - d = d or self.d - try: - val1 = math.asin(wavelength/(2*d)) - val2 = 0.5*math.atan(r/L) - return val1 - val2 - except Exception as e: - return e - - - def calcWavelength(self, r = None, L = None, d = None, theta = None): - r = r or self.r - L = L or self.L - d = d or self.d - theta = theta or self.theta - valin = 0.5*math.atan(r/L) - try: - wavelength = 2*d * math.sin(valin + theta) - return wavelength - except Exception as e: - return e - - - - - - - - - - - \ No newline at end of file diff --git a/gui/dialog/resolution_dialog.py b/gui/dialog/resolution_dialog.py deleted file mode 100644 index f1fab0af..00000000 --- a/gui/dialog/resolution_dialog.py +++ /dev/null @@ -1,247 +0,0 @@ -from qtpy.QtWidgets import * -from qtpy import QtCore -from qtpy import QtGui -from qtpy import QtWidgets -from qtpy.QtCore import * -from qtpy.QtGui import * -import sys -from gui.dialog import Calculator -import typing - -if typing.TYPE_CHECKING: - from lsdcGui import ControlMain - -WINDOW_SIZE = 480 - -#main qtpy window the calculator exists in - - -class CalculatorWindow(QtWidgets.QDialog): - def __init__(self, parent: "ControlMain"): - super(CalculatorWindow, self).__init__(parent) - self.setFixedSize(WINDOW_SIZE,WINDOW_SIZE) - #making radio buttons to choose formula - self.buttonDictionary = {'L': {'picker' : QRadioButton('Caluclate crystal to detector distance')}, - 'd': {'picker': QRadioButton("Calculate resolution")} , - 'theta': {'picker':QRadioButton("Calculate detector 2theta")}, - 'wavelength': {'picker':QRadioButton("Calculate wavelength")}, - 'r':{'value':None}} - - #making lines to hold inputs - # self.r_value_enter = QComboBox() - # self.r_value_enter.setToolTip("Detector Distance") - # self.r_value_enter = QLineEdit() - # self.r_value_enter.setPlaceholderText('Set r value (in mm)') - # self.buttonDictionary['r']['value'] = self.r_value_enter - # self.r_value_enter.setCurrentIndex(1) - - - self.r_value_enter = QLineEdit() - self.r_value_enter.setPlaceholderText('Set r value') - self.buttonDictionary['r']['value'] = self.r_value_enter - self.r_value_enter.setValidator(QDoubleValidator()) - #setting inputs to Double only - - self.L_value_enter = QLineEdit() - self.L_value_enter.setPlaceholderText('Set L value') - self.buttonDictionary['L']['value'] = self.L_value_enter - self.L_value_enter.setValidator(QDoubleValidator()) - - self.d_value_enter = QLineEdit() - self.d_value_enter.setPlaceholderText('Set d value') - self.buttonDictionary['d']['value'] = self.d_value_enter - self.d_value_enter.setValidator(QDoubleValidator()) - - self.theta_value_enter = QLineEdit() - self.theta_value_enter.setPlaceholderText('Set theta value') - self.buttonDictionary['theta']['value'] = self.theta_value_enter - self.theta_value_enter.setValidator(QDoubleValidator()) - - self.wave_value_enter = QLineEdit() - self.wave_value_enter.setPlaceholderText('Set wavelength value') - self.buttonDictionary['wavelength']['value'] = self.wave_value_enter - self.wave_value_enter.setValidator(QDoubleValidator()) - - - self.final_button = QPushButton('Calculate', self) - self.final_button.clicked.connect(self.calculateValue) - - self.bottom_text = QLabel() - self.bottom_text.setText('Enter values and Press button to calculate') - - - #creating calculator object - self.calculator = Calculator() - - - - layout = QVBoxLayout() - layout.addWidget(self.r_value_enter) - for key in self.buttonDictionary: - if 'picker' in self.buttonDictionary[key].keys(): - layout.addWidget(self.buttonDictionary[key]['picker']) - layout.addWidget(self.buttonDictionary[key]['value']) - layout.addWidget(self.final_button) - layout.addWidget(self.bottom_text) - self.setLayout(layout) - #self._createDisplay() - - - - # def _createButtons(self): - # buttonsLayout = QGridLayout() - # self.formula_picker = QRadioButton('Formula') - # self.b2 = QRadioButton("Button2") - - ''' - calls resolution calculator to calculate value depending on inputs from widgets - - -outputs - -value_to_return = value from formula calculated if no problems - -returns nothing if a problem occured, changes bottom_text - ''' - - def calculateValue(self): - checked_key = None - #checking which formula to use - for key in self.buttonDictionary: - if key != 'r' and self.buttonDictionary[key]['picker'].isChecked(): - checked_key = key - if checked_key == None: - self.bottom_text.setText("No calculation specified (press one of the radio buttons)") - return - - #getting values from textboxes r_value text box - # r_value = self.r_value_enter.currentIndex() - # convertValues = [200,244.7] - # # print("r value = {}".format(r_value)) - # #checking if value is a number string or empty string - # r_value = convertValues[r_value] - # r_value = float(r_value) - - r_value = self.r_value_enter.displayText() - # checking if value is a number string or empty string - if r_value == "" or r_value[0].isalpha() == True: - self.bottom_text.setText("formula to calculate {} requires r value".format(checked_key)) - return - elif float(r_value) < 140 or float(r_value) > 350: - self.bottom_text.setText("r value must be between 140 and 350") - return - - r_value = float(r_value) - - - - d_value = self.d_value_enter.displayText() - #checking if value is string or none if not calculating that value (trying to use .isalpha but not when value is None) - if ((d_value == "" or d_value[0].isalpha() == True) and checked_key != 'd') : - self.bottom_text.setText("formula to calculate {} requires d value".format(checked_key)) - return - - l_value = self.L_value_enter.displayText() - if ((l_value == "" or l_value[0].isalpha() == True) and checked_key != 'L'): - self.bottom_text.setText("formula to calculate {} requires L value".format(checked_key)) - return - - theta_value = self.theta_value_enter.displayText() - if ((theta_value == "" or theta_value[0].isalpha() == True)and checked_key != 'theta'): - self.bottom_text.setText("formula to calculate {} requires theta value".format(checked_key)) - return - - wave_value = self.wave_value_enter.displayText() - if ((wave_value == "" or wave_value[0].isalpha() == True) and checked_key != 'wavelength'): - self.bottom_text.setText("formula to calculate {} requires the wavelenght".format(checked_key)) - return - - - #setting value to return if want value returned - value_to_return = None - - if checked_key == 'd': - l_value = float(self.L_value_enter.displayText()) - theta_value = float(self.theta_value_enter.displayText()) - wave_value = float(self.wave_value_enter.displayText()) - - - - - - variableDict = {'L':l_value, 'theta': theta_value, 'wavelength': wave_value, 'r': r_value} - - self.calculator.set_all_variables(variableDict) - d_value = self.calculator.calcD() - value_to_return = d_value - self.d_value_enter.setText(str(d_value)) - self.calculator.set_variables('d', d_value) - - - - - elif checked_key == 'L': - - d_value = float(self.d_value_enter.displayText()) - theta_value = float(self.theta_value_enter.displayText()) - wave_value = float(self.wave_value_enter.displayText()) - - - - - variableDict = {'d':d_value, 'theta': theta_value, 'wavelength': wave_value, 'r': r_value} - - self.calculator.set_all_variables(variableDict) - L_value = self.calculator.calcL() - value_to_return = L_value - self.L_value_enter.setText(str(L_value)) - self.calculator.set_variables('L', L_value) - - elif checked_key == 'theta': - - l_value = float(self.L_value_enter.displayText()) - d_value = float(self.d_value_enter.displayText()) - wave_value = float(self.wave_value_enter.displayText()) - - - variableDict = {'L':l_value, 'd': d_value, 'wavelength': wave_value, 'r': r_value} - - self.calculator.set_all_variables(variableDict) - theta_value = self.calculator.calcTheta() - value_to_return = theta_value - self.theta_value_enter.setText(str(theta_value)) - self.calculator.set_variables('theta', theta_value) - - - - elif checked_key == 'wavelength': - - l_value = float(self.L_value_enter.displayText()) - theta_value = float(self.theta_value_enter.displayText()) - d_value = float(self.d_value_enter.displayText()) - variableDict = {'L':l_value, 'd': d_value, 'theta': theta_value, 'r': r_value} - - self.calculator.set_all_variables(variableDict) - wave_value = self.calculator.calcWavelength() - self.calculator.set_variables('wavelength', wave_value) - value_to_return = wave_value - self.wave_value_enter.setText(str(wave_value)) - - - - - - - - - self.bottom_text.setText("- Done Calculating - \n {} value = {}".format(checked_key, value_to_return)) - return value_to_return - - - - - - -if __name__ == '__main__': - app = QApplication(sys.argv) - window = CalculatorWindow() - window.show() - - app.exec() \ No newline at end of file diff --git a/md2_flyers.py b/md2_flyers.py new file mode 100644 index 00000000..59254c08 --- /dev/null +++ b/md2_flyers.py @@ -0,0 +1,269 @@ +import logging +import os +from collections import deque +import getpass +import grp +from ophyd.sim import NullStatus +from ophyd.status import SubscriptionStatus + +logger = logging.getLogger(__name__) + +DEFAULT_DATUM_DICT = {"data": None, "omega": None} + +INTERNAL_SERIES = 0 +INTERNAL_ENABLE = 1 +EXTERNAL_SERIES = 2 +EXTERNAL_ENABLE = 3 + +class MD2StandardFlyer(): + def __init__(self, md2, detector=None) -> None: + self.name = "MD2StandardFlyer" + self.detector = detector + self.md2 = md2 + self.collection_params = {} + + self._asset_docs_cache = deque() + self._resource_uids = [] + self._datum_counter = None + self._datum_ids = DEFAULT_DATUM_DICT + self._master_file = None + self._master_metadata = [] + + self._collection_dictionary = None + + def kickoff(self): + md2_msg = self.md2.standard_scan(num_images=self.collection_params["total_num_images"], + start_angle=self.collection_params["start_angle"], + scan_range=self.collection_params["scan_range"], + exposure_time=self.collection_params["exposure_time"]) + logger.info(f"md2_msg: {md2_msg}") + return NullStatus() + + def update_parameters(self, total_num_images, start_angle, scan_range, exposure_time): + self.collection_params = { + "total_num_images": total_num_images, + "start_angle": start_angle, + "scan_range": scan_range, + "exposure_time": exposure_time, + } + + def configure_detector(self, file_prefix, data_directory_name): + self.detector.file.external_name.put(file_prefix) + self.detector.file.write_path_template = data_directory_name + + def detector_arm(self, angle_start, img_width, total_num_images, exposure_per_image, + file_prefix, data_directory_name, file_number_start, x_beam, y_beam, + wavelength, det_distance_m): + self.detector.cam.save_files.put(1) + self.detector.cam.sequence_id.put(file_number_start) + self.detector.cam.det_distance.put(det_distance_m) + self.detector.cam.file_owner.put(getpass.getuser()) + self.detector.cam.file_owner_grp.put(grp.getgrgid(os.getgid())[0]) + self.detector.cam.file_perms.put(420) + file_prefix_minus_directory = str(file_prefix) + file_prefix_minus_directory = file_prefix_minus_directory.split("/")[-1] + self.detector.cam.acquire_time.put(exposure_per_image) + self.detector.cam.acquire_period.put(exposure_per_image) + self.detector.cam.num_triggers.put(1) + self.detector.cam.num_images.put(total_num_images) + self.detector.cam.trigger_mode.put( + EXTERNAL_SERIES + ) # must be external_enable to get the correct number of triggers and stop acquire + self.detector.cam.file_path.put(data_directory_name) + self.detector.cam.fw_name_pattern.put(f"{file_prefix_minus_directory}_$id") + self.detector.cam.beam_center_x.put(x_beam) + self.detector.cam.beam_center_y.put(y_beam) + self.detector.cam.omega_incr.put(img_width) + self.detector.cam.omega_start.put(angle_start) + self.detector.cam.wavelength.put(wavelength) + self.detector.file.file_write_images_per_file.put(500) + + #def armed_callback(value, old_value, **kwargs): + # if old_value == 0 and value == 1: + # return True + # return False + + #status = SubscriptionStatus(self.detector.cam.armed, armed_callback, run=False) + #self.detector.cam.acquire.put(1) + #yield status + + def complete(self): + # monitor md2 status, wait for ready or timeout and then return + ready_status = self.md2.ready_status() + timeout = self.collection_params["exposure_time"] + 10 + ready_status.wait(timeout=timeout) + return ready_status + + def describe_collect(self): + return {"stream_name": {}} + #return {self.name: self._collection_dictionary} + + def collect(self): + logger.debug("raster_flyer.collect(): going to unstage now") + yield {"data": {}, "timestamps": {}, "time": 0, "seq_num": 0} + #return self._collection_dictionary + + def unstage(self): + logger.debug("flyer unstaging") + self.collection_params = {} + + def read_configuration(self): + return {} + + def describe_configuration(self): + return {} + + # def collect_asset_docs(self): + # for _ in (): + # yield _ + + def collect_asset_docs(self): + asset_docs_cache = [] + + # Get the Resource which was produced when the detector was staged. + ((name, resource),) = self.detector.file.collect_asset_docs() + + asset_docs_cache.append(("resource", resource)) + self._datum_ids = DEFAULT_DATUM_DICT + # Generate Datum documents from scratch here, because the detector was + # triggered externally by the DeltaTau, never by ophyd. + resource_uid = resource["uid"] + # num_points = int(math.ceil(self.detector.cam.num_images.get() / + # self.detector.cam.fw_num_images_per_file.get())) + + # We are currently generating only one datum document for all frames, that's why + # we use the 0th index below. + # + # Uncomment & update the line below if more datum documents are needed: + # for i in range(num_points): + + seq_id = self.detector.cam.sequence_id.get() + + self._master_file = f"{resource['root']}/{resource['resource_path']}_{seq_id}_master.h5" + if not os.path.isfile(self._master_file): + raise RuntimeError(f"File {self._master_file} does not exist") + + # The pseudocode below is from Tom Caswell explaining the relationship between resource, datum, and events. + # + # resource = { + # "resource_id": "RES", + # "resource_kwargs": {}, # this goes to __init__ + # "spec": "AD-EIGER-MX", + # ...: ..., + # } + # datum = { + # "datum_id": "a", + # "datum_kwargs": {"data_key": "data"}, # this goes to __call__ + # "resource": "RES", + # ...: ..., + # } + # datum = { + # "datum_id": "b", + # "datum_kwargs": {"data_key": "omega"}, + # "resource": "RES", + # ...: ..., + # } + + # event = {...: ..., "data": {"detector_img": "a", "omega": "b"}} + + for data_key in self._datum_ids.keys(): + datum_id = f"{resource_uid}/{data_key}" + self._datum_ids[data_key] = datum_id + datum = { + "resource": resource_uid, + "datum_id": datum_id, + "datum_kwargs": {"data_key": data_key}, + } + asset_docs_cache.append(("datum", datum)) + return tuple(asset_docs_cache) + + def _extract_metadata(self, field="omega"): + with h5py.File(self._master_file, "r") as hf: + return hf.get(f"entry/sample/goniometer/{field}")[()] + +class MD2VectorFlyer(MD2StandardFlyer): + def __init__(self, md2, detector=None) -> None: + super().__init__(md2, detector) + self.name = "MD2VectorFlyer" + + def kickoff(self): + # params used are start_angle, scan_range, exposure_time, start_y, start_z, stop_y, stop_z + md2_msg = self.md2.vector_scan(start_angle=self.collection_params["start_angle"], + scan_range=self.collection_params["scan_range"], + exposure_time=self.collection_params["exposure_time"], + start_cx=self.collection_params["start_cx"], + start_cy=self.collection_params["start_cy"], + start_y=self.collection_params["start_y"], + start_z=self.collection_params["start_z"], + stop_cx=self.collection_params["stop_cx"], + stop_cy=self.collection_params["stop_cy"], + stop_y=self.collection_params["stop_y"], + stop_z=self.collection_params["stop_z"],) + logger.info(f"md2_msg: {md2_msg}") + return NullStatus() + + def update_parameters(self, start_angle, scan_range, exposure_time, start_y, start_z, stop_y, stop_z, start_cx, start_cy, stop_cx, stop_cy): + self.collection_params = { + "start_angle": start_angle, + "scan_range": scan_range, + "exposure_time": exposure_time, + "start_cx": start_cx, + "start_cy": start_cy, + "start_y": start_y, + "start_z": start_z, + "stop_cx": stop_cx, + "stop_cy": stop_cy, + "stop_y": stop_y, + "stop_z": stop_z, + } + +class MD2RasterFlyer(MD2StandardFlyer): + # List of scan parameters values, "/t" separated. The axes start values define the beginning + # of the exposure, that is when all the axes have a steady speed and when the shutter/detector + # are triggered. + # The axes stop values are for the end of detector exposure and define the position at the + # beginning of the deceleration. + # Inputs names: "omega_range", "line_range", "total_uturn_range", "start_omega", "start_y", + # "start_z", "start_cx", "start_cy", "number_of_lines", "frames_per_lines", "exposure_time", + # "invert_direction", "use_centring_table", "use_fast_mesh_scans" + + def __init__(self, md2, detector=None) -> None: + super().__init__(md2, detector) + self.name = "MD2RasterFlyer" + + def kickoff(self): + # params used are start_angle, scan_range, exposure_time, start_y, start_z, stop_y, stop_z + md2_msg = self.md2.raster_scan(omega_range=self.collection_params["omega_range"], + line_range=self.collection_params["line_range"], + total_uturn_range=self.collection_params["total_uturn_range"], + start_omega=self.collection_params["start_omega"], + start_y=self.collection_params["start_y"], + start_z=self.collection_params["start_z"], + start_cx=self.collection_params["start_cx"], + start_cy=self.collection_params["start_cy"], + number_of_lines=self.collection_params["number_of_lines"], + frames_per_line=self.collection_params["frames_per_line"], + exposure_time=self.collection_params["exposure_time"], + invert_direction=self.collection_params["invert_direction"], + use_centring_table=self.collection_params["use_centring_table"], + use_fast_mesh_scans=self.collection_params["use_fast_mesh_scans"]) + logger.info(f"md2_msg: {md2_msg}") + return NullStatus() + + def update_parameters(self, omega_range, line_range, total_uturn_range, start_omega, start_y, start_z, start_cx, start_cy, number_of_lines, frames_per_line, exposure_time, invert_direction, use_centring_table, use_fast_mesh_scans): + self.collection_params = { + "omega_range": omega_range, + "line_range": line_range, + "total_uturn_range": total_uturn_range, + "start_omega": start_omega, + "start_y": start_y, + "start_z": start_z, + "start_cx": start_cx, + "start_cy": start_cy, + "number_of_lines": number_of_lines, + "frames_per_line": frames_per_line, + "exposure_time": exposure_time, + "invert_direction": invert_direction, + "use_centring_table": use_centring_table, + "use_fast_mesh_scans": use_fast_mesh_scans, + } \ No newline at end of file diff --git a/setenergy_lsdc.py b/setenergy_lsdc.py new file mode 100644 index 00000000..40cc41b0 --- /dev/null +++ b/setenergy_lsdc.py @@ -0,0 +1,1004 @@ +from ophyd import (SingleTrigger, ProsilicaDetector, + ImagePlugin, TIFFPlugin, StatsPlugin, ROIPlugin, DetectorBase, HDF5Plugin, + TransformPlugin, ProcessPlugin, AreaDetector) + +from ophyd import Component as Cpt +from ophyd import Device +from ophyd import EpicsSignal, EpicsSignalRO +from ophyd import EpicsMotor +from ophyd import PVPositioner, PVPositionerPC +import bluesky.plan_stubs as bps +import bluesky.plans as bp +import bluesky.preprocessors as bpp +import epics +import numpy as np +import pandas as pd +import socket +import time +import gov_lib +from start_bs import db, gov_robot, govs + +# Machine ========================================================== + +beam_current = EpicsSignal('SR:OPS-BI{DCCT:1}I:Real-I') + +class InsertionDevice(Device): + gap = Cpt(EpicsMotor, '-Ax:Gap}-Mtr', + kind='hinted', name='') + brake = Cpt(EpicsSignal, '}BrakesDisengaged-Sts', + write_pv='}BrakesDisengaged-SP', + kind='omitted', add_prefix=('read_pv', 'write_pv', 'suffix')) + + def set(self, *args, **kwargs): + self.brake.set(1).wait(5) + return self.gap.set(*args, **kwargs) + + def stop(self, *, success=False): + return self.gap.stop(success=success) + +ivu_gap = InsertionDevice('SR:C17-ID:G1{IVU21:2', name='ivu') + +# Photon Local Feedback, sector 17 orbit angle correction onto FMX XBPM1 +class PhotonLocalFeedback(Device): + x_enable = Cpt(EpicsSignal, 'X-FdbkEnabled') + y_enable = Cpt(EpicsSignal, 'Y-FdbkEnabled') + +photon_local_feedback_c17 = PhotonLocalFeedback('SR:APHLA:LBAgent{BUMP:C17-R7X2}', name='photon_local_feedback') + + +# Motors ============================================================ + +class YMotor(Device): + y = Cpt(EpicsMotor, '-Ax:Y}Mtr', labels=['fmx']) + +class XYMotor(Device): + x = Cpt(EpicsMotor, '-Ax:X}Mtr', labels=['fmx']) + y = Cpt(EpicsMotor, '-Ax:Y}Mtr', labels=['fmx']) + +class XYZMotor(XYMotor): + z = Cpt(EpicsMotor, '-Ax:Z}Mtr', labels=['fmx']) + +### 2DO: XZXYMotor -> XZMotor +class XZXYMotor(Device): + x = Cpt(EpicsMotor, '-Ax:X}Mtr', labels=['fmx']) + z = Cpt(EpicsMotor, '-Ax:Z}Mtr', labels=['fmx']) + +class Slits(Device): + b = Cpt(EpicsMotor, '-Ax:B}Mtr', labels=['fmx']) + i = Cpt(EpicsMotor, '-Ax:I}Mtr', labels=['fmx']) + o = Cpt(EpicsMotor, '-Ax:O}Mtr', labels=['fmx']) + t = Cpt(EpicsMotor, '-Ax:T}Mtr', labels=['fmx']) + x_ctr = Cpt(EpicsMotor, '-Ax:XCtr}Mtr', labels=['fmx']) + x_gap = Cpt(EpicsMotor, '-Ax:XGap}Mtr', labels=['fmx']) + y_ctr = Cpt(EpicsMotor, '-Ax:YCtr}Mtr', labels=['fmx']) + y_gap = Cpt(EpicsMotor, '-Ax:YGap}Mtr', labels=['fmx']) + +class DCM(Device): + b = Cpt(EpicsMotor, '-Ax:B}Mtr', labels=['fmx']) + g = Cpt(EpicsMotor, '-Ax:G}Mtr', labels=['fmx']) + p = Cpt(EpicsMotor, '-Ax:P}Mtr', labels=['fmx']) + r = Cpt(EpicsMotor, '-Ax:R}Mtr', labels=['fmx']) + e = Cpt(EpicsMotor, '-Ax:E}Mtr', labels=['fmx']) +# w = Cpt(EpicsMotor, '-Ax:W}Mtr', labels=['fmx']) + +class XYPitchMotor(XYMotor): + pitch = Cpt(EpicsMotor, '-Ax:P}Mtr') + +class KBMirror(Device): + hp = Cpt(EpicsMotor, ':KBH-Ax:P}Mtr') + hr = Cpt(EpicsMotor, ':KBH-Ax:R}Mtr') + hx = Cpt(EpicsMotor, ':KBH-Ax:X}Mtr') + hy = Cpt(EpicsMotor, ':KBH-Ax:Y}Mtr') + vp = Cpt(EpicsMotor, ':KBV-Ax:P}Mtr') + vx = Cpt(EpicsMotor, ':KBV-Ax:X}Mtr') + vy = Cpt(EpicsMotor, ':KBV-Ax:Y}Mtr') + + +class Cover(Device): + close = Cpt(EpicsSignal, 'Cmd:Cls-Cmd') + open = Cpt(EpicsSignal, 'Cmd:Opn-Cmd') + status = Cpt(EpicsSignalRO, 'Pos-Sts') # status: 0 (Not Open), 1 (Open) + +class Shutter(Device): + close = Cpt(EpicsSignal, 'Cmd:Cls-Cmd.PROC') + open = Cpt(EpicsSignal, 'Cmd:Opn-Cmd.PROC') + status = Cpt(EpicsSignalRO, 'Pos-Sts') # status: 0 (Open), 1 (Closed), 2 (Undefined) + +class GoniometerStack(Device): + gx = Cpt(EpicsMotor, '-Ax:GX}Mtr', labels=['fmx']) + gy = Cpt(EpicsMotor, '-Ax:GY}Mtr', labels=['fmx']) + gz = Cpt(EpicsMotor, '-Ax:GZ}Mtr', labels=['fmx']) + o = Cpt(EpicsMotor, '-Ax:O}Mtr', labels=['fmx']) + py = Cpt(EpicsMotor, '-Ax:PY}Mtr', labels=['fmx']) + pz = Cpt(EpicsMotor, '-Ax:PZ}Mtr', labels=['fmx']) + +## Horizontal Double Crystal Monochromator (FMX) +hdcm = DCM('XF:17IDA-OP:FMX{Mono:DCM', name='hdcm') + +# Vertical Double Crystal Monochromator (AMX) +dcm_amx = DCM('XF:17IDA-OP:AMX{Mono:DCM', name='dcm_amx') + + +## Horizontal Focusing Mirror - XYPitchMotor +hfm = XYPitchMotor('XF:17IDA-OP:FMX{Mir:HFM', name='hfm') + +## KB Mirror +kbm = KBMirror('XF:17IDC-OP:FMX{Mir', name='kbm') + + +## 17-ID-A FOE shutter +shutter_foe = Shutter('XF:17ID-PPS:FAMX{Sh:FE}', name='shutter_foe', + read_attrs=['status']) + +## 17-ID-C experimental hutch shutter +shutter_hutch_c = Shutter('XF:17IDA-PPS:FMX{PSh}', name='shutter_hutch_c', + read_attrs=['status']) + +## FMX BCU shutter +shutter_bcu = Shutter('XF:17IDC-ES:FMX{Gon:1-Sht}', name='shutter_bcu', + read_attrs=['status']) + +## Eiger16M detector cover +cover_detector = Cover('XF:17IDC-ES:FMX{Det:FMX-Cover}', name='cover_detector', + read_attrs=['status']) + +## Slits Motions +slits1 = Slits('XF:17IDA-OP:FMX{Slt:1', name='slits1', labels=['fmx']) + +## Light +light = YMotor('XF:17IDC-ES:FMX{Light:1', name='lightY') + +## Goniometer Stack +gonio = GoniometerStack('XF:17IDC-ES:FMX{Gon:1', name='gonio') + +# Detectors =================================================================================== + +keithley = EpicsSignalRO('XF:17IDC-BI:FMX{Keith:1}readFloat', name='keithley') + +class StandardProsilica(SingleTrigger, ProsilicaDetector): + image = Cpt(ImagePlugin, 'image1:') + roi1 = Cpt(ROIPlugin, 'ROI1:') + roi2 = Cpt(ROIPlugin, 'ROI2:') + roi3 = Cpt(ROIPlugin, 'ROI3:') + roi4 = Cpt(ROIPlugin, 'ROI4:') + trans1 = Cpt(TransformPlugin, 'Trans1:') + proc1 = Cpt(ProcessPlugin, 'Proc1:') + stats1 = Cpt(StatsPlugin, 'Stats1:') + stats2 = Cpt(StatsPlugin, 'Stats2:') + stats3 = Cpt(StatsPlugin, 'Stats3:') + stats4 = Cpt(StatsPlugin, 'Stats4:') + stats5 = Cpt(StatsPlugin, 'Stats5:') + tiff = Cpt(TIFFPlugin, 'TIFF1:') + +cam_7 = StandardProsilica('XF:17IDC-ES:FMX{Cam:7}', name='cam_7') +cam_8 = StandardProsilica('XF:17IDC-ES:FMX{Cam:8}', name='cam_8') + +all_standard_pros = [cam_7, cam_8] + +for camera in all_standard_pros: + camera.read_attrs = ['stats1', 'stats2', 'stats3', 'stats4', 'stats5'] + camera.stats1.read_attrs = ['total', 'centroid'] + camera.stats2.read_attrs = ['total', 'centroid'] + camera.stats3.read_attrs = ['total', 'centroid'] + camera.stats4.read_attrs = ['total', 'centroid', 'sigma_x', 'sigma_y'] + camera.stats5.read_attrs = ['total', 'centroid'] + camera.stats4.centroid.read_attrs = ['x', 'y'] + camera.tiff.read_attrs = [] + +# BPM ======================================================================================= + +class Bpm(Device): + x = Cpt(EpicsSignalRO, 'PosX:MeanValue_RBV') + y = Cpt(EpicsSignalRO, 'PosY:MeanValue_RBV') + a = Cpt(EpicsSignalRO, 'Current1:MeanValue_RBV') + b = Cpt(EpicsSignalRO, 'Current2:MeanValue_RBV') + c = Cpt(EpicsSignalRO, 'Current3:MeanValue_RBV') + d = Cpt(EpicsSignalRO, 'Current4:MeanValue_RBV') + sum_x = Cpt(EpicsSignalRO, 'SumX:MeanValue_RBV') + sum_y = Cpt(EpicsSignalRO, 'SumY:MeanValue_RBV') + sum_all = Cpt(EpicsSignalRO, 'SumAll:MeanValue_RBV') + +bpm1 = Bpm('XF:17IDA-BI:FMX{BPM:1}', name='bpm1') + +bpm1.sum_all.kind = 'hinted' + +bpm1_sum_all_precision = EpicsSignal('XF:17IDA-BI:FMX{BPM:1}SumAll:MeanValue_RBV.PREC') +bpm1_sum_all_precision.put(10) + +# Attenuators, CRL ========================================================================= + +class Transmission(Device): + energy = Cpt(EpicsSignal, 'Energy-SP') # PV only used for debugging. Attenuator uses Bragg axis energy + transmission = Cpt(EpicsSignal, 'Trans-SP') + set_trans = Cpt(EpicsSignal, 'Cmd:Set-Cmd.PROC') + +## Dummy Attenuator - for read/write_lut() and XF:17ID-ES:FMX{Misc-LUT:atten}X-Wfm/Y-Wfm +class AttenuatorLUT(Device): + done = Cpt(EpicsSignalRO, '}attenDone') + +class AttenuatorBCU(Device): + a1 = Cpt(EpicsMotor, '-Ax:1}Mtr', labels=['fmx']) + a2 = Cpt(EpicsMotor, '-Ax:2}Mtr', labels=['fmx']) + a3 = Cpt(EpicsMotor, '-Ax:3}Mtr', labels=['fmx']) + a4 = Cpt(EpicsMotor, '-Ax:4}Mtr', labels=['fmx']) + done = Cpt(EpicsSignalRO, '}attenDone') + +## BCU Transmission +trans_bcu = Transmission('XF:17IDC-OP:FMX{Attn:BCU}', name='trans_bcu', + read_attrs=['transmission']) +## RI Transmission +trans_ri = Transmission('XF:17IDC-OP:FMX{Attn:RI}', name='trans_ri', + read_attrs=['transmission']) + +## Dummy Attenuator - for read/write_lut() and XF:17ID-ES:FMX{Misc-LUT:atten}X-Wfm/Y-Wfm +atten = AttenuatorLUT('XF:17IDC-OP:FMX{Attn:BCU', name='atten', + read_attrs=['done']) + +## BCU Attenuator +atten_bcu = AttenuatorBCU('XF:17IDC-OP:FMX{Attn:BCU', name='atten_bcu', + read_attrs=['done', 'a1', 'a2', 'a3', 'a4'], + labels=['fmx']) + +# Utility ================================================================================= + +class BeamlineCalibrations(Device): + LoMagCal = Cpt(EpicsSignal, 'LoMagCal}') + HiMagCal = Cpt(EpicsSignal, 'HiMagCal}') + +BL_calibration = BeamlineCalibrations('XF:17ID-ES:FMX{Misc-', + name='BL_calibration', + read_attrs=['LoMagCal', 'HiMagCal']) + +def blStrGet(): + """ + Return beamline string + + blStr: 'AMX' or 'FMX' + + Beamline is determined by querying hostname + """ + hostStr = socket.gethostname() + if hostStr.startswith('xf17id2'): + blStr = 'FMX' + elif hostStr.startswith('xf17id1'): + blStr = 'AMX' + else: + print('Error - this code must be executed on one of the -ca1 machines') + blStr = -1 + + return blStr + + +# Plans to set beamline energy ======================================================================= + +## Helper functions for set_energy and alignment + +def find_peak(det, mot, start, stop, steps): + print(f"Scanning {mot.name} vs {det.name}...") + + uid = yield from bp.relative_scan([det], mot, start, stop, steps) + + sp = '_gap_user_setpoint' if mot is ivu_gap else '_user_setpoint' + output = '_sum_all' if det is bpm1 else '' + data = np.array(db[uid].table()[[det.name+output, mot.name+sp]])[1:] + + peak_idx = np.argmax(data[:, 0]) + peak_x = data[peak_idx, 1] + peak_y = data[peak_idx, 0] + + if mot is ivu_gap: + m = mot.gap + else: + m = mot + print(f"Found peak for {m.name} at {peak_x} {m.egu} [BPM reading {peak_y}]") + return peak_x, peak_y + +## Lookup tables, Last good positions + + +LUT_fmt = "XF:17ID-ES:FMX{{Misc-LUT:{}}}{}-Wfm" +LGP_fmt = "XF:17ID-ES:FMX{{Misc-LGP:{}}}Pos-SP" + +LUT_valid = (ivu_gap.gap, hdcm.g, hdcm.r, hdcm.p, hfm.y, hfm.x, hfm.pitch, kbm.hy, kbm.vx, atten) +LGP_valid = (kbm.hp, kbm.hx, kbm.vp, kbm.vy) + +LUT_valid_names = [m.name for m in LUT_valid] + ['ivu_gap_off'] +LGP_valid_names = [m.name for m in LGP_valid] + +def get_energy(): + """ + Returns the current photon energy in eV derived from the DCM Bragg angle + """ + + blStr = blStrGet() + if blStr == -1: return -1 + + if blStr == 'AMX': + energy = dcm_amx.e.user_readback.get() + elif blStr == 'FMX': + energy = hdcm.e.user_readback.get() + + return energy + +def read_lut(name): + """ + Reads the LookUp table values for a specific motor + """ + if name not in LUT_valid_names: + raise ValueError('name must be one of {}'.format(LUT_valid_names)) + + x, y = [epics.caget(LUT_fmt.format(name, axis)) for axis in 'XY'] + return pd.DataFrame({'Energy':x, 'Position': y}) + + +def write_lut(name, energy, position): + """ + Writes to the LookUp table for a specific motor + """ + if name not in LUT_valid_names: + raise ValueError('name must be one of {}'.format(LUT_valid_names)) + + if len(energy) != len(position): + raise ValueError('energy and position must have the same number of points') + + epics.caput(LUT_fmt.format(name, 'X'), energy) + epics.caput(LUT_fmt.format(name, 'Y'), position) + + +def read_lgp(name): + """ + Reads the Last Good Position value for a specific motor + """ + if name not in LGP_valid_names: + raise ValueError('name must be one of {}'.format(LGP_valid_names)) + + return epics.caget(LGP_fmt.format(name)) + +def write_lgp(name, position): + """ + Writes to the Last Good Position value for a specific motor + """ + if name not in LGP_valid_names: + raise ValueError('name must be one of {}'.format(LGP_valid_names)) + + return epics.caput(LGP_fmt.format(name), position) + + + +def setE_motors_FMX(energy): + """ + Sets undulator, hdcm, HFM and KB settings for a certain energy from a lookup table + + energy: Photon energy [eV] + + Lookup tables and variables are set in a settings notebook: + settings/set_energy setup FMX.ipynb + + Examples: + setE_motors_FMX(12660) + """ + + # (FMX specific) + LUT = {m: [epics.caget(LUT_fmt.format(m.name, axis)) + for axis in 'XY'] + for m in (ivu_gap.gap, hdcm.g, hdcm.r, hdcm.p, hfm.y, hfm.x, hfm.pitch, kbm.hy, kbm.vx)} + + LUT_offset = [epics.caget(LUT_fmt.format('ivu_gap_off', axis)) for axis in 'XY'] + + LGP = {m: epics.caget(LGP_fmt.format(m.name)) + for m in (kbm.hp, kbm.hx, kbm.vp, kbm.vy)} + + # Remove CRLs if going to energy < 9 keV (FMX specific) + if energy < 9001: + set_beamsize('V0','H0') + + # Lookup Table + def lut(motor): + if motor is ivu_gap: + return motor, np.interp(energy, *LUT[motor.gap]) + else: + return motor, np.interp(energy, *LUT[motor]) + + # Last Good Position + def lgp(motor): + return motor, LGP[motor] + + # (FMX specific) + yield from bps.mv( + *lut(ivu_gap), # Set IVU Gap interpolated position + hdcm.e, energy, # Set Bragg Energy pseudomotor + *lut(hdcm.g), # Set DCM Gap interpolated position + *lut(hdcm.r), # Set DCM Roll interpolated position # MF 20180331 + *lut(hdcm.p), # Set Pitch interpolated position + + # Set HFM from interpolated positions + *lut(hfm.x), + *lut(hfm.y), + *lut(hfm.pitch), + + # Set KB from interpolated positions + *lut(kbm.vx), + *lut(kbm.hy), + + # Set KB from known good setpoints + *lgp(kbm.vy), *lgp(kbm.vp), + *lgp(kbm.hx), *lgp(kbm.hp) + ) + + +def dcm_rock(dcm_p_range=0.03, dcm_p_points=51, logging=True, altDetector=False): + """ + Scan DCM crystal 2 pitch to maximize flux on BPM1 + dcm_rock() runs both with the AMX DCM_AMX and the FMX hdcm + + Parameters + ---------- + + Optional arguments: + dcm_p_range: DCM rocking curve range [mrad]. Default 0.03 mrad + dcm_p_points: DCM rocking curve points. Default 51 + altDetector: If True, uses alternate detector, BPM1 at AMX and Keithley at FMX + + Examples + -------- + + RE(dcm_rock()) + RE(dcm_rock(altDetector = True)) + RE(dcm_rock(dcm_p_range=0.035, dcm_p_points=71)) + """ + blStr = blStrGet() + if blStr == -1: return -1 + + if blStr == 'AMX': + rock_mot = dcm_amx.p + rock_det = bpm1 if altDetector is True else keithley + elif blStr == 'FMX': + rock_mot = hdcm.p + rock_det = keithley if altDetector is True else bpm1 + + energy = get_energy() + + LUT = {m: [epics.caget(LUT_fmt.format(m.name, axis)) + for axis in 'XY'] + for m in (rock_mot, )} + + # Lookup Table + def lut(motor): + return motor, np.interp(energy, *LUT[motor]) + + yield from bps.mv( + *lut(rock_mot) # Set Pitch interpolated position + ) + + # Decorate find_peaks to play along with our plot and plot the peak location + def find_peak_inner(detector, motor, start, stop, num): + if detector == bpm1: + det_name = detector.name+'_sum_all' + else: + det_name = detector.name + mot_name = motor.name+'_user_setpoint' + + # 2DO: Comment out when used within LSDC + def inner(): + peak_x, peak_y = yield from find_peak(detector, motor, start, stop, num) + return peak_x, peak_y + return inner() + + # Scan DCM Pitch + peak_x, peak_y = yield from find_peak_inner(rock_det, rock_mot, -dcm_p_range, dcm_p_range, dcm_p_points) + yield from bps.mv(rock_mot, peak_x) + + # (FMX specific) + if logging: + print('Energy = {:.1f} eV'.format(energy)) + print('hdcm cr2 pitch = {:.3f} mrad'.format(rock_mot.user_readback.get())) + if rock_det == bpm1: + print('BPM1 sum = {:.4g} A'.format(bpm1.sum_all.get())) + elif rock_det == keithley: + time.sleep(2.0) # Range switching is slow + print('Keithley current = {:.4g} A'.format(keithley.get())) + + + + +def ivu_gap_scan(start, end, steps, detector=bpm1, goToPeak=True): + """ + Scans the IVU21 gap against a detector, and moves the gap to the peak plus a + energy dependent look-up table set offset + + Parameters + ---------- + + start: float + The starting position (um) of the VU21 undulator gap scan + + end: float + The end position (um) of the VU21 undulator gap scan + + steps: int + Number of steps in the scan + + detector: ophyd detector + The ophyd detector for the scan. Default is bpm1. Only setup up for the quad BPMs right now + + goToPeak: boolean + If True, go to the peak plus energy-tabulated offset. If False, go back to pre-scan value. + + Examples + -------- + + RE(ivu_gap_scan(7350, 7600, 70)) + RE(ivu_gap_scan(7350, 7600, 70, goToPeak=False)) + RE(ivu_gap_scan(7350, 7600, 70, detector=bpm1)) + """ + + energy = get_energy() + + motor=ivu_gap + if start-1 < motor.gap.low_limit: + start = motor.gap.low_limit + 1 + print('start violates lowest limit, set to %.1f' % start + ' um') + + LUT_offset = [epics.caget(LUT_fmt.format('ivu_gap_off', axis)) for axis in 'XY'] + + # Decorate find_peaks to play along with our plot and plot the peak location + def find_peak_inner(detector, motor, start, stop, num): + det_name = detector.name+'_sum_all' + mot_name = motor.gap.name+'_user_setpoint' if motor is ivu_gap else motor.name+'_user_setpoint' + + # Prevent going below the lower limit or above the high limit + if motor is ivu_gap: + step_size = (stop - start) / (num - 1) + while motor.gap.user_setpoint.get() + start < motor.gap.low_limit: + start += 5*step_size + stop += 5*step_size + + while motor.gap.user_setpoint.get() + stop > motor.gap.high_limit: + start -= 5*step_size + stop -= 5*step_size + + # 2DO: Comment out when used within LSDC + def inner(): + peak_x, peak_y = yield from find_peak(detector, motor, start, stop, num) + return peak_x, peak_y + return inner() + + # Remember pre-scan value + gapPreStart=motor.gap.user_readback.get() + + # Move to start + yield from bps.mv(motor, start) + + # Scan IVU Gap + peak_x, peak_y = yield from find_peak_inner(detector, ivu_gap, 0, (end-start), steps) + + # Go to peak + if goToPeak==True: + peakoffset_x = (peak_x + np.interp(energy, *LUT_offset)) + yield from bps.mv(ivu_gap, peakoffset_x) + print('Gap set to peak + tabulated offset: %.1f' % peakoffset_x + ' um') + else: + yield from bps.mv(ivu_gap, gapPreStart) + print('Gap set to pre-scan value: %.1f' % gapPreStart + ' um') + + +def setELsdc(energy, + dcm_p_range=0.03, dcm_p_points=51, altDetector=False, + ivuGapStartOff=70, ivuGapEndOff=70, ivuGapSteps=31, + transSet='All', beamCenterAlign=True, slit1Set=True): + """ + Automated photon energy change. Master function calling four subroutines: + * setE_motors_FMX(): Set photon delivery system motor positions for a chosen energy + * dcm_rock(): Go to peak of monochromator rocking curve + * ivu_gap_scan(): Go to peak of undulator gap + * beam_center_align(): Set LSDC crosshair to beam center + Move rotation axis to beam heightS + Set governor Gonio Y Work position + + Requirements + ------------ + Governor in state SA + FOE and hutch photon shutter open + + + Parameters + ---------- + + energy: Photon energy [eV] + + dcm_p_range: Scan range of DCM Crystal 2 Pitch [mrad], default = 0.03 + dcm_p_points: Number of scan points of SCM rocking curve, default = 51 + altDetector: Rocking curve to use alternate detector between BPM1 and endstation diode, default = False + + ivuGapStartOff: IVU gap scan start offset from tabulated position [um], default = 70 + ivuGapEndOff: IVU gap scan end offset from tabulated position [um], default = 70 + ivuGapSteps: IVU gap scan steps, default = 31 + + transSet: FMX only: Set to 'RI' if there is a problem with the BCU attenuator. + FMX only: Set to 'BCU' if there is a problem with the RI attenuator. + Set to 'None' if there are problems with all = attenuators. + Operator then has to choose a flux by hand that will not saturate scinti + default = 'All' + + beamCenterAlign: Set to False to skip beam_center_align() step (like the old set_energy() routine) + Default True + + slit1Set: Set to False to skip setting Slit 1 Gap values + Default True + + Examples + -------- + + RE(setE(7110)) + RE(setE(10000, transSet='None')) + RE(setE(12660, transSet='RI')) + RE(setE(20000, ivuGapStartOff=100, ivuGapEndOff=150, ivuGapSteps=91)) + RE(setE(9000, beamCenterAlign=False)) + RE(setE(12660, beamCenterAlign=False, slit1Set=False)) + """ + + desired_states = ("SA", "AB") + if not gov_robot.state.get() in desired_states: + print(f'Governor state not in one of {desired_states}, exiting') + return -1 + + # Store initial Slit 1 gap positions + if slit1Set: + slits1XGapOrg = slits1.x_gap.user_readback.get() + slits1YGapOrg = slits1.y_gap.user_readback.get() + + print('Setting FMX motor positions') + try: + yield from setE_motors_FMX(energy) + except: + print('setE_motors_FMX() failed') + raise + else: + print('setE_motors_FMX() successful') + time.sleep(1) + + # Check for pre-conditions for dcm_rock() and ivu_gap_scan() + if shutter_foe.status.get(): + print('FOE shutter closed. Has to be open for this to work. Exiting') + return -1 + + # DCM rocking curve + print('Rocking monochromator') + yield from dcm_rock(dcm_p_range=dcm_p_range, dcm_p_points=dcm_p_points, altDetector=altDetector) + time.sleep(1) + + # Undulator gap scan + print('Scanning undulator gap') + start = ivu_gap.gap.user_readback.get() - ivuGapStartOff + end = ivu_gap.gap.user_readback.get() + ivuGapEndOff + try: + yield from ivu_gap_scan(start, end, ivuGapSteps, goToPeak=True) + except: + print('ivu_gap_scan() failed') + raise + else: + print('ivu_gap_scan() successful') + time.sleep(1) + + # Activate sector 17 photon local feedback + photon_local_feedback_c17.x_enable.put(1) + photon_local_feedback_c17.y_enable.put(1) + + # Align LSDC microscope center to beam center + if beamCenterAlign: + # Check for pre-conditions for beam_center_align() + if shutter_hutch_c.status.get(): + print('Experiment hutch shutter closed. Has to be open for this to work. Exiting') + return -1 + + print('Aligning beam center') + yield from beam_center_align(transSet=transSet) + + # Restore initial Slit 1 gap positions + if slit1Set: + yield from bps.mv(slits1.x_gap, slits1XGapOrg) # Move Slit 1 X to original position + yield from bps.mv(slits1.y_gap, slits1YGapOrg) # Move Slit 1 Y to original position + + +# Alignment =========================================================================================== + +## Plans to align beam and goniometer for LSDC + +def centroid_avg(stats): + """ + Read centroid X and Y 10x and return mean of centroids. + + stats : stats method of ophyd camera object to use, e.g. cam_8.stats4 + + Examples + -------- + centroid_avg(cam_8.stats4) + centroidY = centroid_avg(cam_8.stats4)[1] + """ + + centroidXArr = np.zeros(10) + centroidYArr = np.zeros(10) + for i in range(0, 10): + centroidXArr[i] = stats.centroid.x.get() + centroidYArr[i] = stats.centroid.y.get() + # print('Centroid X = {:.6g} px'.format(centroidXArr[i]), ', Centroid Y = {:.6g} px'.format(centroidYArr[i])) + time.sleep(0.2) + CentroidX = centroidXArr.mean() + CentroidY = centroidYArr.mean() + print('Mean centroid X = {:.6g} px'.format(CentroidX)) + print('Mean centroid Y = {:.6g} px'.format(CentroidY)) + + return CentroidX, CentroidY + + +def detectorCoverClose(): + """ + Closes the Detector Cover + """ + yield from bps.mv(cover_detector.close, 1) + + while cover_detector.status.get() == 1: + #print(cover_detector.status.get()) + time.sleep(0.5) + + return + +def detectorCoverOpen(): + """ + Opens the Detector Cover + """ + yield from bps.mv(cover_detector.open, 1) + + while cover_detector.status.get() != 1: + #print(cover_detector.status.get()) + time.sleep(0.5) + + return + + +def trans_set(transmission, trans = trans_bcu): + """ + Sets the Attenuator transmission + """ + + e_dcm = get_energy() + if e_dcm < 5000 or e_dcm > 30000: + print('Monochromator energy out of range. Must be within 5000 - 30000 eV. Exiting.') + return + + yield from bps.mv(trans.energy, e_dcm) # This energy PV is only used for debugging + yield from bps.mv(trans.transmission, transmission) + yield from bps.mv(trans.set_trans, 1) + + if trans == trans_bcu: + while atten_bcu.done.get() != 1: + time.sleep(0.5) + + print('Attenuator = ' + trans.name + ', Transmission set to %.3f' % trans.transmission.get()) + return + + +def trans_get(trans = trans_bcu): + """ + Returns the Attenuator transmission + """ + + transmission = trans.transmission.get() + + print('Attenuator = ' + trans.name + ', Transmission = %.3f' % transmission) + return transmission + + +def transDefaultGet(energy): + """ + Returns the default transmission to avoid saturation of the scintillator + + energy: X-ray energy [eV] + + The look up table is set in settings/set_energy setup FMX.ipynb + """ + + # This reads from: + # XF:17ID-ES:FMX{Misc-LUT:atten}X-Wfm + # XF:17ID-ES:FMX{Misc-LUT:atten}Y-Wfm + # + # atten is a dummy motor just for this purpose. + # To be replaced by trans_bcu and corresponding new PVs + + transLUT = read_lut('atten') + transDefault = np.interp(energy,transLUT['Energy'],transLUT['Position']) + + return transDefault + + +# Beam align functions + +def beam_center_align(transSet='All'): + """ + Corrects alignment of goniometer and LSDC center point after a beam drift + + Requirements + ------------ + * No sample mounted. Goniometer will be moved inboard out of sample position + * Governor in SA state + + Parameters + ---------- + transSet: FMX only: Set to 'RI' if there is a problem with the BCU attenuator. + FMX only: Set to 'BCU' if there is a problem with the RI attenuator. + Set to 'None' if there are problems with all attenuators. + Operator then has to choose a flux by hand that will not saturate scinti + default = 'All' + + Examples + -------- + RE(beam_center_align()) + RE(beam_center_align(transSet='None')) + RE(beam_center_align(transSet='RI')) + """ + # TODO: + # * Consider running with BCU attenuators only + # * Check for Vis screen actuators out + # * Check for C-hutch shutter open + # * Check for ROI2 exceeding camera border. + # Check how this affects the ROI centering move, and whether we can correct for that + + # Which beamline? + blStr = blStrGet() + if blStr == -1: return -1 + + if blStr == 'FMX': + if transSet not in ['All', 'None', 'BCU', 'RI']: + print('transSet must be one of: All, None, BCU, RI') + return -1 + else: + if transSet not in ['All', 'None']: + print('transSet must be one of: All, None') + return -1 + + desired_states = ("SA", "AB") + if not gov_robot.state.get() in desired_states: + print(f'Governor state not in one of {desired_states}, exiting.') + return -1 + + # Check for beam after DCM: BPM1 total current + if bpm1.sum_all.get() < 1e-7: + print('Intensity after DCM low. BPM1 total current <1e-7 A.', + 'Check FOE shutter, and rocking curve, then repeat.', + 'Exiting.') + return -1 + + print('Closing detector cover') + detectorCoverClose() + + # Transition to Governor state AB (Auto-align Beam) + gov_lib.setGovRobot(gov_robot, 'AB') + + # Set beam transmission that avoids scintillator saturation + # Default values are defined in settings as lookup table + if transSet != 'None': + transDefault = transDefaultGet( get_energy() ) + if blStr == 'FMX': + if transSet in ['All', 'BCU']: + transOrgBCU = trans_get(trans=trans_bcu) + if transSet in ['All', 'RI']: + transOrgRI = trans_get(trans=trans_ri) + yield from trans_set(transDefault, trans=trans_ri) + if transSet == 'BCU': + yield from trans_set(transDefault, trans=trans_bcu) + if transSet == 'All': + yield from trans_set(1, trans=trans_bcu) + else: + transOrgBCU = trans_get(trans=trans_bcu) + yield from trans_set(transDefault, trans=trans_bcu) + + # Retract backlight + yield from bps.mv(light.y,govs.gov.Robot.dev.li.target_Out.get()) + print('Light Y Out') + + # TODO: use "yield from bps.mv(...)" instead of .put(...) below. + + # ROI1 centroid plugin does not work + # Copy ROI1 geometry to ROI4 and use ROI4 centroid plugin + cam_8.roi4.min_xyz.min_x.put(cam_8.roi1.min_xyz.min_x.get()) + cam_8.roi4.min_xyz.min_y.put(cam_8.roi1.min_xyz.min_y.get()) + cam_8.roi4.size.x.put(cam_8.roi1.size.x.get()) + cam_8.roi4.size.y.put(cam_8.roi1.size.y.get()) + + yield from bps.mv(shutter_bcu.open, 1) + print('BCU Shutter Open') + time.sleep(1) + + # Check for focused beam on scinti. Do nothing if stats 4 max intensity < 20 counts + # TODO: Verify 20 counts threshold for more settings + if cam_8.stats4.max_value.get() < 20: + print('Max intensity < 20 counts.', + 'Check beam intensity and focus on scinti, then repeat.', + 'No changes made.') + else: + # Camera calibration [um/px] + hiMagCal = BL_calibration.HiMagCal.get() + loMagCal = BL_calibration.LoMagCal.get() + + # Read centroids + beamHiMagCentroid = centroid_avg(cam_8.stats4) + beamHiMagCentroidX = beamHiMagCentroid[0] + beamHiMagCentroidY = beamHiMagCentroid[1] + time.sleep(1) + + # Get beam shift on Hi Mag + # Assume the LSDC centering crosshair is in the center of the FOV + # This works as long as cam_8 ROI1 does not hit the edge of the cam_8 image + beamHiMagDiffX = beamHiMagCentroidX - (cam_8.roi4.size.x.get()/2) + beamHiMagDiffY = beamHiMagCentroidY - (cam_8.roi4.size.y.get()/2) + + # Do nothing if we see a too large shift + if beamHiMagDiffX>100 or beamHiMagDiffY>100: + print('Beam centroid change > 100 px detected.', + 'No changes made. Manual beam center correction needed.') + beamHiMagDiffX=0 + beamHiMagDiffY=0 + + # # Do nothing if we would walk ROI2 for Mag 3 off the top camera edge + # if (cam_8.roi2.min_xyz.min_y.get() + beamHiMagDiffY + cam_8.roi2.size.y.get()) > 1215 + # print('ROI2 for Mag 3 too high.', + # 'Move beam towards center of Hi Mag, or align beam center by hand', + # 'No changes made. Manual beam center correction needed.') + # beamHiMagDiffX=0 + # beamHiMagDiffY=0 + # + # # Do nothing if we would walk ROI2 for Mag 3 off the bottom camera edge + # if (cam_8.roi2.min_xyz.min_y.get() + beamHiMagDiffY < 1 + # print('ROI2 for Mag 3 too low.', + # 'Move beam towards center of Hi Mag, or align beam center by hand', + # 'No changes made. Manual beam center correction needed.') + # beamHiMagDiffX=0 + # beamHiMagDiffY=0 + + # Correct Mag 4 (cam_8 ROI1) + # Adjust cam_8 ROI1 min_y, LSDC uses this for the Mag4 FOV. + cam_8.roi1.min_xyz.min_x.put(cam_8.roi1.min_xyz.min_x.get() + beamHiMagDiffX) + cam_8.roi1.min_xyz.min_y.put(cam_8.roi1.min_xyz.min_y.get() + beamHiMagDiffY) + + # Correct Mag 3 (cam_8 ROI2) + # This works as long as cam_8 ROI2 does not hit the edge of the cam_8 image + cam_8.roi2.min_xyz.min_x.put(cam_8.roi2.min_xyz.min_x.get() + beamHiMagDiffX) + cam_8.roi2.min_xyz.min_y.put(cam_8.roi2.min_xyz.min_y.get() + beamHiMagDiffY) + + # Get beam shift on Lo Mag from Hi Mag shift and calibration factor ratio + beamLoMagDiffX = beamHiMagDiffX * hiMagCal/loMagCal + beamLoMagDiffY = beamHiMagDiffY * hiMagCal/loMagCal + + # Correct Mag 1 (cam_7 ROI2) + cam_7.roi2.min_xyz.min_x.put(cam_7.roi2.min_xyz.min_x.get() + beamLoMagDiffX) + cam_7.roi2.min_xyz.min_y.put(cam_7.roi2.min_xyz.min_y.get() + beamLoMagDiffY) + + # Correct Mag 2 (cam_7 ROI3) + cam_7.roi3.min_xyz.min_x.put(cam_7.roi3.min_xyz.min_x.get() + beamLoMagDiffX) + cam_7.roi3.min_xyz.min_y.put(cam_7.roi3.min_xyz.min_y.get() + beamLoMagDiffY) + + time.sleep(3) + + # Adjust Gonio Y so rotation axis is again aligned to beam + gonioYDiff = beamHiMagDiffY * hiMagCal + posGyOld = govs.gov.Robot.dev.gy.target_Work.get() + posGyNew = posGyOld + gonioYDiff + yield from bps.mv(gonio.gy, posGyNew) # Move Gonio Y to new position + govs.gov.Robot.dev.gy.target_Work.set(posGyNew) # Set Governor Gonio Y Work position to new value + print('Gonio Y difference = %.3f' % gonioYDiff) + + yield from bps.mv(shutter_bcu.close, 1) + print('BCU Shutter Closed') + + # Transition to Governor state SA (Sample Alignment) + gov_lib.setGovRobot(gov_robot, 'SA') + + # Set previous beam transmission + if transSet != 'None': + if blStr == 'FMX': + if transSet in ['All', 'RI']: + yield from trans_set(transOrgRI, trans=trans_ri) + if transSet in ['All', 'BCU']: + yield from trans_set(transOrgBCU, trans=trans_bcu) + else: + yield from trans_set(transOrgBCU, trans=trans_bcu) \ No newline at end of file From 44c5bf24fe69c52b6f687ac3a8a69884b365fb3e Mon Sep 17 00:00:00 2001 From: NYSBC-Rudra <133771854+NYSBC-Rudra@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:36:52 -0500 Subject: [PATCH 79/79] in line with md2 --- daq_main_common.py | 90 ++---------- db_lib.py | 49 +------ gui/control_main.py | 265 +++++++++++++++++++--------------- gui/dewar_tree.py | 265 ++++++++++++++-------------------- gui/dialog/screen_defaults.py | 4 +- gui/dialog/staff_screen.py | 58 +++++--- gui/dialog/user_screen.py | 24 +-- ispybLib.py | 16 +- runFastDPH5.py | 4 +- 9 files changed, 331 insertions(+), 444 deletions(-) diff --git a/daq_main_common.py b/daq_main_common.py index b4c05dbc..8532df4c 100755 --- a/daq_main_common.py +++ b/daq_main_common.py @@ -1,28 +1,26 @@ -import _thread -import atexit -import json -import logging -import os import string import sys +import os import time -import traceback - -import beamline_lib -import beamline_support -import config_params +import _thread import daq_lib import daq_utils import det_lib -from beamline_lib import * -from daq_lib import * +import beamline_support +import beamline_lib +from start_bs import plt +import config_params +import atexit +import traceback #imports to get useful things into namespace for server from daq_macros import * -from gov_lib import setGovRobot +from daq_lib import * from robot_lib import * -from start_bs import gov_robot, plt +from beamline_lib import * +from gov_lib import setGovRobot +import logging logger = logging.getLogger(__name__) sitefilename = "" @@ -31,68 +29,10 @@ immediate_command_list = [] z = 25 -def setGovState(state): - setGovRobot(gov_robot, state) - - -functions = [anneal, - set_beamsize, - importSpreadsheet, - mvaDescriptor, - setTrans, - loop_center_xrec, - autoRasterLoop, - snakeRaster, - ispybLib.insertRasterResult, - backlightBrighter, - backlightDimmer, - changeImageCenterHighMag, - changeImageCenterLowMag, - center_on_click, - runDCQueue, - warmupGripper, - dryGripper, - enableDewarTscreen, - parkGripper, - stopDCQueue, - continue_data_collection, - mountSample, - unmountSample, - reprocessRaster, - fastDPNodes, - spotNodes, - unmountCold, - openPort, - set_beamcenter, - closePorts, - clearMountedSample, - recoverRobot, - rebootEMBL, - restartEMBL, - openGripper, - closeGripper, - homePins, - setSlit1X, - setSlit1Y, - testRobot, - setGovState] - -whitelisted_functions: "Dict[str, Callable]" = { - func.__name__: func for func in functions -} def execute_command(command_s): - logger.info("executing command: %s" % command_s) - try: - command: "dict[str, Any]" = json.loads(command_s) - func = whitelisted_functions[command["function"]] - except Exception as e: - logger.exception(f"Error in function parsing and lookup: {e}") - - try: - func(*command["args"], **command["kwargs"]) - except Exception as e: - logger.exception(f"Error executing {command_s}: {e}") + logger.info('executing command: %s' % command_s) + exec(command_s) def pybass_init(): @@ -243,4 +183,4 @@ def run_server(): beamline_support.pvPut(immediate_comm_pv,"\n") comm_pv.add_callback(comm_cb) immediate_comm_pv.add_callback(comm2_cb) - process_commands(0.5) + process_commands(0.5) \ No newline at end of file diff --git a/db_lib.py b/db_lib.py index a0321618..1c2cb0ae 100755 --- a/db_lib.py +++ b/db_lib.py @@ -1,15 +1,16 @@ -import logging import os + import time + +import six + import uuid -from collections import defaultdict import amostra.client.commands as acc import conftrak.client.commands as ccc -import conftrak.exceptions -import six from analysisstore.client.commands import AnalysisClient - +import conftrak.exceptions +import logging logger = logging.getLogger(__name__) #12/19 - Skinner inherited this from Hugo, who inherited it from Matt. Arman wrote the underlying DB and left BNL in 2018. @@ -485,43 +486,6 @@ def getContainerByID(container_id): c = getContainers(filters={'uid': container_id})[0] return c -def get_dewar_tree_data(dewar_name, beamline): - """ - returns all data required to show dewar tree data with minimum number of database accesses - """ - dewar_data = getContainers(filters={"name": dewar_name, "owner": beamline})[0] - - puck_ids = [ - pid for pid in dewar_data.get("content", []) if pid - ] # removes blank ids - pucks = getContainers(filters={"uid": {"$in": puck_ids}}) - - # Create a mega list of sample ids from puck information - sample_ids = [ - sample_id - for puck in pucks - for sample_id in puck.get("content", []) - if sample_id - ] - - # Get all sample info in one call - params = {"uid": {"$in": sample_ids}} - samples = sample_ref.find(as_document=False, **params) - - # Get all request info in one call - params = {"sample": {"$in": sample_ids}, "state": "active"} - reqs = list(request_ref.find(**params)) - - # Assemble data into dictionaries - puck_data = {puck["uid"]: puck for puck in pucks} - sample_data = {sample["uid"]: sample for sample in samples} - request_data = defaultdict(list) - for req in reqs: - request_data[req["sample"]].append(req) - - return dewar_data, puck_data, sample_data, request_data - - def getQueue(beamlineName): """ @@ -863,4 +827,3 @@ def deleteCompletedRequestsforSample(sid): if (requestList[i]["priority"] == -1): #good to clean up completed requests after unmount if (requestList[i]["protocol"] == "raster" or requestList[i]["protocol"] == "vector"): deleteRequest(requestList[i]['uid']) - diff --git a/gui/control_main.py b/gui/control_main.py index 1bee3e41..5d65d1fb 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -1,12 +1,11 @@ import _thread import functools -import json import logging import math import os import sys import time -from typing import Dict, List, Optional +import threading from queue import Queue import cv2 @@ -20,7 +19,8 @@ from qtpy import QtCore, QtGui, QtWidgets from qtpy.QtCore import QModelIndex, QRectF, Qt, QTimer from qtpy.QtGui import QIntValidator -from qtpy.QtWidgets import QApplication, QCheckBox, QFrame, QGraphicsPixmapItem +from qtpy.QtWidgets import QCheckBox, QFrame, QGraphicsPixmapItem, QApplication +from devices import GonioDevice, CameraDevice, MD2Device, LightDevice, MD2ApertureDevice import albulaUtils import daq_utils @@ -29,9 +29,9 @@ from config_params import ( CRYOSTREAM_ONLINE, HUTCH_TIMER_DELAY, + SERVER_CHECK_DELAY, RASTER_GUI_XREC_FILL_DELAY, SAMPLE_TIMER_DELAY, - SERVER_CHECK_DELAY, VALID_DET_DIST, VALID_EXP_TIMES, VALID_TOTAL_EXP_TIMES, @@ -53,7 +53,7 @@ ) from gui.raster import RasterCell, RasterGroup from QPeriodicTable import QPeriodicTable -from threads import RaddoseThread, ServerCheckThread, VideoThread +from threads import RaddoseThread, VideoThread, ServerCheckThread logger = logging.getLogger() try: @@ -62,8 +62,6 @@ logger.error("lsdcGui: ISPYB import error, %s" % e) - - def get_request_object_escan( reqObj, symbol, @@ -233,7 +231,7 @@ def __init__(self): ] self.mountedPin_pv.put(mountedPin) self.rasterExploreDialog = RasterExploreDialog() - + self.userScreenDialog = UserScreenDialog(self) self.detDistMotorEntry.getEntry().setText( self.detDistRBVLabel.getEntry().text() ) # this is to fix the current val being overwritten by reso @@ -243,7 +241,6 @@ def __init__(self): self.changeControlMasterCB(1) self.controlMasterCheckBox.setChecked(True) self.XRFInfoDict = self.parseXRFTable() # I don't like this - # self.dewarTree.refreshTreeDewarView() def setGuiValues(self, values): for item, value in values.items(): @@ -410,12 +407,10 @@ def createSampleTab(self): self.osc_end_ledit.textChanged[str].connect( functools.partial(self.totalExpChanged, "oscEnd") ) - if daq_utils.beamline == "fmx": - self.osc_end_ledit.textChanged.connect(self.calcLifetimeCB) - hBoxColParams1.addWidget(colStartLabel) - hBoxColParams1.addWidget(self.osc_start_ledit) - hBoxColParams1.addWidget(self.colEndLabel) - hBoxColParams1.addWidget(self.osc_end_ledit) + #hBoxColParams1.addWidget(colStartLabel) + #hBoxColParams1.addWidget(self.osc_start_ledit) + #hBoxColParams1.addWidget(self.colEndLabel) + #hBoxColParams1.addWidget(self.osc_end_ledit) hBoxColParams2 = QtWidgets.QHBoxLayout() colRangeLabel = QtWidgets.QLabel("Oscillation Width:") colRangeLabel.setFixedWidth(140) @@ -1532,7 +1527,8 @@ def createSampleTab(self): lambda frame: self.updateCam(self.pixmap_item_HutchTop, frame) ) self.hutchTopCamThread.start() - serverCheckThread = ServerCheckThread(parent=self, delay=SERVER_CHECK_DELAY) + serverCheckThread = ServerCheckThread( + parent=self, delay=SERVER_CHECK_DELAY) serverCheckThread.visit_dir_changed.connect(QApplication.instance().quit) serverCheckThread.start() @@ -1549,7 +1545,9 @@ def annealButtonCB(self): try: ftime = float(self.annealTime_ledit.text()) if ftime >= 0.1 and ftime <= 5.0: - self.send_to_server("anneal", [ftime]) + comm_s = "anneal(" + str(ftime) + ")" + logger.info(comm_s) + self.send_to_server(comm_s) else: self.popupServerMessage( "Anneal time must be between 0.1 and 5.0 seconds." @@ -1832,12 +1830,18 @@ def saveVidSnapshotCB( if rasterHeatJpeg == None: if reqID != None: filePrefix = db_lib.getRequestByID(reqID)["request_obj"]["file_prefix"] - imagePath = f"{getBlConfig('visitDirectory')}/snapshots/{filePrefix}{int(now)}.jpg" + imagePath = ( + f"{getBlConfig('visitDirectory')}/snapshots/{filePrefix}{int(now)}.jpg" + ) else: if self.dataPathGB.prefix_ledit.text() != "": - imagePath = f"{getBlConfig('visitDirectory')}/snapshots/{self.dataPathGB.prefix_ledit.text()}{int(now)}.jpg" + imagePath = ( + f"{getBlConfig('visitDirectory')}/snapshots/{self.dataPathGB.prefix_ledit.text()}{int(now)}.jpg" + ) else: - imagePath = f"{getBlConfig('visitDirectory')}/snapshots/capture{int(now)}.jpg" + imagePath = ( + f"{getBlConfig('visitDirectory')}/snapshots/capture{int(now)}.jpg" + ) else: imagePath = rasterHeatJpeg logger.info("saving " + imagePath) @@ -2626,9 +2630,13 @@ def protoRadioToggledCB(self, text): pass def beamsizeComboActivatedCB(self, text): - comm_s = 'set_beamsize("' + str(text[0:2]) + '","' + str(text[2:4]) + '")' - logger.info(comm_s) - self.send_to_server(comm_s) + if daq_utils.beamline == "nyx": + index = self.beamsizeComboBox.findText(str(text)) + self.aperture.current_index.put(index) + else: + comm_s = 'set_beamsize("' + str(text[0:2]) + '","' + str(text[2:4]) + '")' + logger.info(comm_s) + self.send_to_server(comm_s) def protoComboActivatedCB(self, text): self.showProtParams() @@ -2729,7 +2737,10 @@ def popImportDialogCB(self): ) #self.timerSample.start(SAMPLE_TIMER_DELAY) if fname != "": - self.send_to_server("importSpreadsheet", [fname[0], daq_utils.owner]) + logger.info(fname) + comm_s = f'importSpreadsheet("{fname[0]}", "{daq_utils.owner}")' + logger.info(comm_s) + self.send_to_server(comm_s) def setUserModeCB(self): self.vidActionDefineCenterRadio.setEnabled(False) @@ -2813,10 +2824,12 @@ def dewarViewToggleCheckCB(self): def moveOmegaCB(self): comm_s = ( - 'mvaDescriptor("omega",' + 'omegaMoveAbs(' + str(self.sampleOmegaMoveLedit.getEntry().text()) + ")" ) + logger.info(comm_s) + self.send_to_server(comm_s) def moveEnergyCB(self): energyRequest = float(str(self.energy_ledit.text())) @@ -2824,7 +2837,9 @@ def moveEnergyCB(self): self.popupServerMessage("Energy change must be less than 10 ev") return else: - self.send_to_server("mvaDescriptor", ["energy", float(self.energy_ledit.text())]) + comm_s = 'mvaDescriptor("energy",' + str(self.energy_ledit.text()) + ")" + logger.info(comm_s) + self.send_to_server(comm_s) def setLifetimeCB(self, lifetime): if hasattr(self, "sampleLifetimeReadback_ledit"): @@ -2848,16 +2863,16 @@ def calcLifetimeCB(self): except Exception as e: logger.info(f"Exception while calculating sample flux {e}") logger.info("sample flux = " + str(sampleFlux)) - # Read vector length only if the vector protocol is chosen - vecLen = 0 - if self.protoVectorRadio.isChecked(): - try: - vecLen = float(self.vecLenLabelOutput.text()) - except: - pass - try: - wedge = float(self.osc_end_ledit.text()) + vecLen_s = self.vecLenLabelOutput.text() + if vecLen_s != "---": + vecLen = float(vecLen_s) + else: + vecLen = 0 + except: + vecLen = 0 + wedge = float(self.osc_end_ledit.text()) + try: raddose_thread = RaddoseThread( parent=self, beamsizeV=3.0, @@ -2887,18 +2902,22 @@ def setTransCB(self): except ValueError as e: self.popupServerMessage("Please enter a valid number") return - self.send_to_server("setTrans", [float(self.transmission_ledit.text())]) + comm_s = "setTrans(" + str(self.transmission_ledit.text()) + ")" + logger.info(comm_s) + self.send_to_server(comm_s) def setDCStartCB(self): currentPos = float(self.sampleOmegaRBVLedit.getEntry().text()) % 360.0 self.setGuiValues({"osc_start": currentPos}) def moveDetDistCB(self): - self.send_to_server("mvaDescriptor", - [ - "detectorDist", - float(self.detDistMotorEntry.getEntry().text()), - ]) + comm_s = ( + 'mvaDescriptor("detectorDist",' + + str(self.detDistMotorEntry.getEntry().text()) + + ")" + ) + logger.info(comm_s) + self.send_to_server(comm_s) def omegaTweakNegCB(self): tv = float(self.omegaTweakVal_ledit.text()) @@ -2954,14 +2973,17 @@ def omegaTweakCB(self, tv): self.popupServerMessage("You don't have control") def autoCenterLoopCB(self): - self.send_to_server("loop_center_xrec") + logger.info("auto center loop") + self.send_to_server("loop_center_xrec()") def autoRasterLoopCB(self): self.selectedSampleID = self.selectedSampleRequest["sample"] - self.send_to_server("autoRasterLoop", [self.selectedSampleID]) + comm_s = "autoRasterLoop(" + str(self.selectedSampleID) + ")" + self.send_to_server(comm_s) def runRastersCB(self): - self.send_to_server("snakeRaster", [self.selectedSampleRequest["uid"]]) + comm_s = "snakeRaster(" + str(self.selectedSampleRequest["uid"]) + ")" + self.send_to_server(comm_s) def drawInteractiveRasterCB(self): # any polygon for now, interactive or from xrec for i in range(len(self.polyPointItems)): @@ -3037,7 +3059,10 @@ def center3LoopCB(self): logger.info("3-click center loop") self.threeClickCount = 1 self.click3Button.setStyleSheet("background-color: yellow") - self.send_to_server('mvaDescriptor("omega",0)') + if(daq_utils.exporter_enabled): + self.md2.exporter.cmd("startManualSampleCentring", "") + else: + self.send_to_server('mvaDescriptor("omega",0)') def fillPolyRaster( self, rasterReq, waitTime=1 @@ -3214,7 +3239,10 @@ def takeRasterSnapshot(self, rasterReq): reqID=rasterReq["uid"], rasterHeatJpeg=jpegImageFilename, ) - self.send_to_server(f"ispybLib.insertRasterResult('{rasterReq['uid']}', '{visitName}')") + try: + ispybLib.insertRasterResult(rasterReq, visitName) + except Exception as e: + logger.error(f"Exception while writing raster result: {e}") def reFillPolyRaster(self): rasterEvalOption = str(self.rasterEvalComboBox.currentText()) @@ -3313,10 +3341,10 @@ def selectAllCenterCB(self): self.centeringMarksList[i]["graphicsItem"].setSelected(True) def lightUpCB(self): - self.send_to_server("backlightBrighter") + self.send_to_server("backlightBrighter()") def lightDimCB(self): - self.send_to_server("backlightDimmer") + self.send_to_server("backlightDimmer()") def eraseRastersCB(self): if self.rasterList != []: @@ -3721,21 +3749,38 @@ def pixelSelect(self, event): True ) # because it's easy to forget defineCenter is on if self.zoom4Radio.isChecked(): - self.send_to_server( - "changeImageCenterHighMag", [x_click, y_click, 1] + comm_s = ( + "changeImageCenterHighMag(" + + str(x_click) + + "," + + str(y_click) + + ",1)" ) elif self.zoom3Radio.isChecked(): - self.send_to_server( - "changeImageCenterHighMag", [x_click, y_click, 0] + comm_s = ( + "changeImageCenterHighMag(" + + str(x_click) + + "," + + str(y_click) + + ",0)" ) if self.zoom2Radio.isChecked(): - self.send_to_server( - "changeImageCenterLowMag", [x_click, y_click, 1] + comm_s = ( + "changeImageCenterLowMag(" + + str(x_click) + + "," + + str(y_click) + + ",1)" ) elif self.zoom1Radio.isChecked(): - self.send_to_server( - "changeImageCenterLowMag", [x_click, y_click, 0] + comm_s = ( + "changeImageCenterLowMag(" + + str(x_click) + + "," + + str(y_click) + + ",0)" ) + self.send_to_server(comm_s) return if self.vidActionRasterDefRadio.isChecked(): self.click_positions.append(event.pos()) @@ -3763,20 +3808,28 @@ def pixelSelect(self, event): if self.threeClickCount > 0: # 3-click centering self.threeClickCount = self.threeClickCount + 1 - comm_s = f'center_on_click({correctedC2C_x},{correctedC2C_y},{fov["x"]},{fov["y"]},source="screen",jog=90,viewangle={current_viewangle})' + if daq_utils.exporter_enabled: + correctedC2C_x = x_click + ((daq_utils.screenPixX/2) - (self.centerMarker.x() + self.centerMarkerCharOffsetX)) + correctedC2C_y = y_click + ((daq_utils.screenPixY/2) - (self.centerMarker.y() + self.centerMarkerCharOffsetY)) + lsdc_x = daq_utils.screenPixX + lsdc_y = daq_utils.screenPixY + md2_x = self.md2.center_pixel_x.get() * 2 + md2_y = self.md2.center_pixel_y.get() * 2 + scale_x = md2_x / lsdc_x + scale_y = md2_y / lsdc_y + correctedC2C_x = correctedC2C_x * scale_x + correctedC2C_y = correctedC2C_y * scale_y + self.md2.centring_click.put(f"{correctedC2C_x} {correctedC2C_y}") + if self.threeClickCount == 4: + self.threeClickCount = 0 + self.click3Button.setStyleSheet("background-color: None") + return + else: + comm_s = f'center_on_click({correctedC2C_x},{correctedC2C_y},{fov["x"]},{fov["y"]},source="screen",jog=90,viewangle={current_viewangle})' else: - comm_s = ( - "center_on_click", - [ - correctedC2C_x, - correctedC2C_y, - fov["x"], - fov["y"], - {"source": "screen", "maglevel": 0, "viewangle": current_viewangle}, - ], - ) + comm_s = f'center_on_click({correctedC2C_x},{correctedC2C_y},{fov["x"]},{fov["y"]},source="screen",maglevel=0,viewangle={current_viewangle})' if not self.vidActionRasterExploreRadio.isChecked(): - self.aux_send_to_server(*comm_s) + self.aux_send_to_server(comm_s) if self.threeClickCount == 4: self.threeClickCount = 0 self.click3Button.setStyleSheet("background-color: None") @@ -4334,24 +4387,19 @@ def collectQueueCB(self): if currentRequest == {}: self.addRequestsToAllSelectedCB() logger.info("running queue") - self.send_to_server("runDCQueue") - + self.send_to_server("runDCQueue()") def warmupGripperCB(self): - self.send_to_server("warmupGripper") - + self.send_to_server("warmupGripper()") def dryGripperCB(self): - self.send_to_server("dryGripper") - + self.send_to_server("dryGripper()") def enableTScreenGripperCB(self): - self.send_to_server("enableDewarTscreen") - + self.send_to_server("enableDewarTscreen()") def parkGripperCB(self): - self.send_to_server("parkGripper") - + self.send_to_server("parkGripper()") def restartServerCB(self): if self.controlEnabled(): @@ -4588,15 +4636,14 @@ def puckToDewarCB(self): def stopRunCB(self): logger.info("stopping collection") - self.aux_send_to_server("stopDCQueue", [1]) + self.aux_send_to_server("stopDCQueue(1)") def stopQueueCB(self): logger.info("stopping queue") if self.pauseQueueButton.text() == "Continue": - self.aux_send_to_server("continue_data_collection") + self.aux_send_to_server("continue_data_collection()") else: - self.aux_send_to_server("stopDCQueue", [2]) - + self.aux_send_to_server("stopDCQueue(2)") def mountSampleCB(self): if getBlConfig("mountEnabled") == 0: @@ -4611,8 +4658,7 @@ def mountSampleCB(self): else: # No sample ID found, do nothing logger.info("No sample selected, cannot mount") return - self.send_to_server("mountSample", [self.selectedSampleID]) - + self.send_to_server('mountSample("' + str(self.selectedSampleID) + '")') self.zoom2Radio.setChecked(True) self.zoomLevelToggledCB("Zoom2") self.protoComboBox.setCurrentIndex(self.protoComboBox.findText(str("standard"))) @@ -4620,8 +4666,7 @@ def mountSampleCB(self): def unmountSampleCB(self): logger.info("unmount sample") - self.send_to_server("unmountSample") - + self.send_to_server("unmountSample()") def refreshCollectionParams(self, selectedSampleRequest, validate_hdf5=True): reqObj = selectedSampleRequest["request_obj"] @@ -4736,15 +4781,14 @@ def refreshCollectionParams(self, selectedSampleRequest, validate_hdf5=True): ) > 5.0 ): - - self.send_to_server( - "mvaDescriptor", - [ - "omega", - selectedSampleRequest["request_obj"]["rasterDef"]["omega"], - ], + comm_s = ( + 'mvaDescriptor("omega",' + + str( + selectedSampleRequest["request_obj"]["rasterDef"]["omega"] + ) + + ")" ) - + self.send_to_server(comm_s) if str(reqObj["protocol"]) == "eScan": try: self.escan_steps_ledit.setText(str(reqObj["steps"])) @@ -5344,41 +5388,24 @@ def controlEnabled(self): and self.controlMasterCheckBox.isChecked() ) - def send_to_server(self, function_name: str, args: "Optional[List]" = None, kwargs: "Optional[Dict]" = None): - if function_name == "lockControl": + def send_to_server(self, s): + if s == "lockControl": self.controlMaster_pv.put(0 - self.processID) return - if function_name == "unlockControl": + if s == "unlockControl": self.controlMaster_pv.put(self.processID) return if self.controlEnabled(): time.sleep(0.01) - message = self.generate_server_message(function_name, args, kwargs) - logger.info(f"send_to_server: {message}") - self.comm_pv.put(message) + logger.info("send_to_server: %s" % s) + self.comm_pv.put(s) else: self.popupServerMessage("You don't have control") - def generate_server_message( - self, function_name: str, args: "Optional[List]" = None, kwargs: "Optional[Dict]" = None - ) -> str: - if not args: - args = [] - if not kwargs: - kwargs = {} - return json.dumps( - { - "function": function_name, - "args": args, - "kwargs": kwargs, - } - ) - - def aux_send_to_server(self, function_name: str, args: "Optional[List]" = None, kwargs: "Optional[Dict]" = None): + def aux_send_to_server(self, s): if self.controlEnabled(): time.sleep(0.01) - message = self.generate_server_message(function_name, args, kwargs) - logger.info(f"aux_send_to_server: {message}") - self.immediate_comm_pv.put(message) + logger.info("aux_send_to_server: %s" % s) + self.immediate_comm_pv.put(s) else: self.popupServerMessage("You don't have control") \ No newline at end of file diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py index ff0d1d9f..82406e1a 100644 --- a/gui/dewar_tree.py +++ b/gui/dewar_tree.py @@ -1,20 +1,12 @@ -import getpass import logging -import os import typing -import requests from qtpy import QtCore, QtGui, QtWidgets from qtpy.QtCore import Qt import daq_utils import db_lib -from config_params import ( - DEWAR_SECTORS, - IS_STAFF, - PUCKS_PER_DEWAR_SECTOR, - SAMPLE_TIMER_DELAY, -) +from config_params import DEWAR_SECTORS, PUCKS_PER_DEWAR_SECTOR, SAMPLE_TIMER_DELAY if typing.TYPE_CHECKING: from lsdcGui import ControlMain @@ -104,17 +96,20 @@ def refreshTreeDewarView(self): puck = "" collectionRunning = False self.model.clear() - dewar_data, puck_data, sample_data, request_data = db_lib.get_dewar_tree_data( + dewarContents = db_lib.getContainerByName( daq_utils.primaryDewarName, daq_utils.beamline - ) - parentItem = self.model.invisibleRootItem() - for i, puck_id in enumerate( - dewar_data["content"] - ): # dewar contents is the list of puck IDs - puck = "" - puckName = "" - if puck_id: - puck = puck_data[puck_id] + )["content"] + for i in range(0, len(dewarContents)): # dewar contents is the list of puck IDs + parentItem = self.model.invisibleRootItem() + if dewarContents[i] == "": + puck = "" + puckName = "" + else: + if dewarContents[i] not in containerDict: + puck = db_lib.getContainerByID(dewarContents[i]) + containerDict[dewarContents[i]] = puck + else: + puck = containerDict[dewarContents[i]] puckName = puck["name"] index_s = "%d%s" % ( (i) / self.pucksPerDewarSector + 1, @@ -129,10 +124,99 @@ def refreshTreeDewarView(self): parentItem.appendRow(item) parentItem = item if puck != "" and puckName != "private": - puckContents = puck.get("content", []) - self.add_samples_to_puck_tree( - puckContents, item, index_s, sample_data, request_data - ) + puckContents = puck["content"] + puckSize = len(puckContents) + for j in range(0, len(puckContents)): # should be the list of samples + if puckContents[j] != "": + if puckContents[j] not in sampleNameDict: + sampleName = db_lib.getSampleNamebyID(puckContents[j]) + sampleNameDict[puckContents[j]] = sampleName + else: + sampleName = sampleNameDict[puckContents[j]] + position_s = str(j + 1) + "-" + sampleName + item = QtGui.QStandardItem( + QtGui.QIcon( + ":/trolltech/styles/commonstyle/images/file-16.png" + ), + position_s, + ) + item.setData( + puckContents[j], 32 + ) # just stuck sampleID there, but negate it to diff from reqID + item.setData("sample", 33) + if puckContents[j] == self.parent.mountedPin_pv.get(): + item.setForeground(QtGui.QColor("red")) + font = QtGui.QFont() + font.setItalic(True) + font.setOverline(True) + font.setUnderline(True) + item.setFont(font) + parentItem.appendRow(item) + if puckContents[j] == self.parent.mountedPin_pv.get(): + mountedIndex = self.model.indexFromItem(item) + if ( + puckContents[j] == self.parent.selectedSampleID + ): # looking for the selected item + logger.info("found " + str(self.parent.SelectedItemData)) + selectedSampleIndex = self.model.indexFromItem(item) + sampleRequestList = db_lib.getRequestsBySampleID( + puckContents[j] + ) + for k in range(len(sampleRequestList)): + if not ("protocol" in sampleRequestList[k]["request_obj"]): + continue + col_item = QtGui.QStandardItem( + QtGui.QIcon( + ":/trolltech/styles/commonstyle/images/file-16.png" + ), + sampleRequestList[k]["request_obj"]["file_prefix"] + + "_" + + sampleRequestList[k]["request_obj"]["protocol"], + ) + col_item.setData(sampleRequestList[k]["uid"], 32) + col_item.setData("request", 33) + col_item.setFlags( + Qt.ItemIsUserCheckable + | Qt.ItemIsEnabled + | Qt.ItemIsEditable + | Qt.ItemIsSelectable + ) + if sampleRequestList[k]["priority"] == 99999: + col_item.setCheckState(Qt.Checked) + col_item.setBackground(QtGui.QColor("green")) + selectedIndex = self.model.indexFromItem( + col_item + ) ##attempt to leave it on the request after collection + + collectionRunning = True + self.parent.refreshCollectionParams( + sampleRequestList[k], validate_hdf5=False + ) + elif sampleRequestList[k]["priority"] > 0: + col_item.setCheckState(Qt.Checked) + col_item.setBackground(QtGui.QColor("white")) + elif sampleRequestList[k]["priority"] < 0: + col_item.setCheckable(False) + col_item.setBackground(QtGui.QColor("cyan")) + else: + col_item.setCheckState(Qt.Unchecked) + col_item.setBackground(QtGui.QColor("white")) + item.appendRow(col_item) + if ( + sampleRequestList[k]["uid"] + == self.parent.SelectedItemData + ): # looking for the selected item, this is a request + selectedIndex = self.model.indexFromItem(col_item) + else: # this is an empty spot, no sample + position_s = str(j + 1) + item = QtGui.QStandardItem( + QtGui.QIcon( + ":/trolltech/styles/commonstyle/images/file-16.png" + ), + position_s, + ) + item.setData("", 32) + parentItem.appendRow(item) self.setModel(self.model) if selectedSampleIndex != None and collectionRunning == False: self.setCurrentIndex(selectedSampleIndex) @@ -172,139 +256,6 @@ def refreshTreeDewarView(self): self.collapseAll() self.scrollTo(self.currentIndex(), QtWidgets.QAbstractItemView.PositionAtCenter) - def add_samples_to_puck_tree( - self, puckContents, parentItem: QtGui.QStandardItem, index_label - ): - # Method will attempt to add samples to the puck. If you don't belong to the proposal, - # it will not add samples and clear the puck information - selectedIndex = None - mountedIndex = None - selectedSampleIndex = None - collectionRunning = False - for j, sample_id in enumerate(puckContents): - if not sample_id: - # this is an empty spot, no sample - position_s = str(j + 1) - item = QtGui.QStandardItem(QtGui.QIcon(ICON), position_s) - item.setData("", 32) - parentItem.appendRow(item) - continue - - sample = sampleDict.get( - sample_id, - sampleDict.setdefault(sample_id, db_lib.getSampleByID(sample_id)), - ) - - if not IS_STAFF and not self.is_proposal_member(sample["proposalID"]): - # If the user is not part of the proposal and is not staff, don't fill tree - # Clear the puck information and don't make it selectable - parentItem.setText(index_label) - current_flags = parentItem.flags() - parentItem.setFlags(current_flags & ~Qt.ItemFlag.ItemIsSelectable) # type: ignore - position_s = f'{j+1}-{sample.get("name", "")}' - item = QtGui.QStandardItem( - QtGui.QIcon(ICON), - position_s, - ) - return - - parentItem.setText(f"{index_label} pass-{sample['proposalID']}") - - position_s = f'{j+1}-{sample.get("name", "")}' - item = QtGui.QStandardItem( - QtGui.QIcon(ICON), - position_s, - ) - # just stuck sampleID there, but negate it to diff from reqID - item.setData(sample_id, 32) - item.setData("sample", 33) - if sample_id == self.parent.mountedPin_pv.get(): - self.set_mounted_sample(item) - parentItem.appendRow(item) - if sample_id == self.parent.mountedPin_pv.get(): - mountedIndex = self.model.indexFromItem(item) - # looking for the selected item - if sample_id == self.parent.selectedSampleID: - logger.info("found " + str(self.parent.SelectedItemData)) - selectedSampleIndex = self.model.indexFromItem(item) - sampleRequestList = db_lib.getRequestsBySampleID(sample_id) - for request in sampleRequestList: - if not ("protocol" in request["request_obj"]): - continue - col_item = self.create_request_item(request) - if request["priority"] == 99999: - selectedIndex = self.model.indexFromItem( - col_item - ) ##attempt to leave it on the request after collection - collectionRunning = True - item.appendRow(col_item) - if ( - request["uid"] == self.parent.SelectedItemData - ): # looking for the selected item, this is a request - selectedIndex = self.model.indexFromItem(col_item) - - current_index = None - if not collectionRunning: - if selectedSampleIndex: - current_index = selectedSampleIndex - elif mountedIndex: - current_index = mountedIndex - item = self.model.itemFromIndex(mountedIndex) - self.set_mounted_sample(item) - elif selectedIndex: - current_index = selectedIndex - elif collectionRunning and mountedIndex: - current_index = mountedIndex - - if current_index: - self.setCurrentIndex(current_index) - self.parent.row_clicked(current_index) - - def is_proposal_member(self, proposal_id) -> bool: - # Check if the user running LSDC is part of the sample's proposal - if proposal_id not in self.proposal_membership: - r = requests.get(f"{os.environ['NSLS2_API_URL']}/proposal/{proposal_id}") - r.raise_for_status() - response = r.json() - if "users" in response and getpass.getuser() in [ - user["username"] for user in response["users"] if "username" in user - ]: - self.proposal_membership[proposal_id] = True - else: - logger.info(f"Users not found in response: {response}") - self.proposal_membership[proposal_id] = False - return self.proposal_membership[proposal_id] - - def create_request_item(self, request) -> QtGui.QStandardItem: - col_item = QtGui.QStandardItem( - QtGui.QIcon(ICON), - request["request_obj"]["file_prefix"] - + "_" - + request["request_obj"]["protocol"], - ) - col_item.setData(request["uid"], 32) - col_item.setData("request", 33) - col_item.setFlags( - Qt.ItemFlag.ItemIsUserCheckable # type:ignore - | Qt.ItemFlag.ItemIsEnabled - | Qt.ItemFlag.ItemIsEditable - | Qt.ItemFlag.ItemIsSelectable - ) - if request["priority"] == 99999: - col_item.setCheckState(Qt.CheckState.Checked) - col_item.setBackground(QtGui.QColor("green")) - self.parent.refreshCollectionParams(request, validate_hdf5=False) - elif request["priority"] > 0: - col_item.setCheckState(Qt.CheckState.Checked) - col_item.setBackground(QtGui.QColor("white")) - elif request["priority"] < 0: - col_item.setCheckable(False) - col_item.setBackground(QtGui.QColor("cyan")) - else: - col_item.setCheckState(Qt.CheckState.Unchecked) - col_item.setBackground(QtGui.QColor("white")) - return col_item - def refreshTreePriorityView( self, ): # "item" is a sample, "col_items" are requests which are children of samples. @@ -527,4 +478,4 @@ def getSelectedSample(self): elif str(item.data(33)) == "request": selectedSampleRequest = db_lib.getRequestByID(item.data(32)) selectedSampleID = selectedSampleRequest["sample"] - return selectedSampleID + return selectedSampleID \ No newline at end of file diff --git a/gui/dialog/screen_defaults.py b/gui/dialog/screen_defaults.py index 6a86e881..7d636525 100644 --- a/gui/dialog/screen_defaults.py +++ b/gui/dialog/screen_defaults.py @@ -202,7 +202,7 @@ def reprocessRasterRequestCB(self): try: reqID = self.parent.selectedSampleRequest["uid"] self.parent.drawPolyRaster(db_lib.getRequestByID(reqID)) - self.parent.send_to_server("reprocessRaster", [reqID]) + self.parent.send_to_server('reprocessRaster("' + str(reqID) + '")') except: pass @@ -271,4 +271,4 @@ def checkEntryState(self, *args, **kwargs): color = "#f6989d" # red else: color = "#ffffff" # white - sender.setStyleSheet("QLineEdit { background-color: %s }" % color) + sender.setStyleSheet("QLineEdit { background-color: %s }" % color) \ No newline at end of file diff --git a/gui/dialog/staff_screen.py b/gui/dialog/staff_screen.py index 31cf0b61..4c731733 100644 --- a/gui/dialog/staff_screen.py +++ b/gui/dialog/staff_screen.py @@ -226,13 +226,15 @@ def getFastDPNodeList(self): return nodeList def setFastDPNodesCB(self): - self.parent.send_to_server( - "fastDPNodes", - [ - int(self.fastDPNodeEntryList[i].text()) - for i in range(self.fastDPNodeCount) - ], - ) + comm_s = "fastDPNodes(" + for i in range(0, self.fastDPNodeCount): + comm_s = comm_s + str(self.fastDPNodeEntryList[i].text()) + if i == self.fastDPNodeCount - 1: + comm_s = comm_s + ")" + else: + comm_s = comm_s + "," + logger.info(comm_s) + self.parent.send_to_server(comm_s) def lockGuiCB(self): self.parent.send_to_server("lockControl") @@ -241,46 +243,54 @@ def unLockGuiCB(self): self.parent.send_to_server("unlockControl") def setSpotNodesCB(self): - self.parent.send_to_server( - "spotNodes", - [int(self.spotNodeEntryList[i].text()) for i in range(self.spotNodeCount)], - ) + comm_s = "spotNodes(" + for i in range(0, self.spotNodeCount): + comm_s = comm_s + str(self.spotNodeEntryList[i].text()) + if i == self.spotNodeCount - 1: + comm_s = comm_s + ")" + else: + comm_s = comm_s + "," + logger.info(comm_s) + self.parent.send_to_server(comm_s) def unmountColdCB(self): - self.parent.send_to_server("unmountCold") + self.parent.send_to_server("unmountCold()") def openPort1CB(self): - self.parent.send_to_server("openPort", [1]) + self.parent.send_to_server("openPort(1)") def setBeamcenterCB(self): self.parent.send_to_server( - "set_beamcenter", - [self.beamcenterX_ledit.text(), self.beamcenterY_ledit.text()], + "set_beamcenter (" + + str(self.beamcenterX_ledit.text()) + + "," + + str(self.beamcenterY_ledit.text()) + + ")" ) def closePortsCB(self): - self.parent.send_to_server("closePorts") + self.parent.send_to_server("closePorts()") def clearMountedSampleCB(self): - self.parent.send_to_server("clearMountedSample") + self.parent.send_to_server("clearMountedSample()") def recoverRobotCB(self): - self.parent.aux_send_to_server("recoverRobot") + self.parent.aux_send_to_server("recoverRobot()") def rebootEMBL_CB(self): - self.parent.aux_send_to_server("rebootEMBL") + self.parent.aux_send_to_server("rebootEMBL()") def restartEMBL_CB(self): - self.parent.send_to_server("restartEMBL") + self.parent.send_to_server("restartEMBL()") def openGripper_CB(self): - self.parent.send_to_server("openGripper") + self.parent.send_to_server("openGripper()") def closeGripper_CB(self): - self.parent.send_to_server("closeGripper") + self.parent.send_to_server("closeGripper()") def homePinsCB(self): - self.parent.send_to_server("homePins") + self.parent.send_to_server("homePins()") def robotOnCheckCB(self, state): if state == QtCore.Qt.Checked: @@ -352,4 +362,4 @@ def screenDefaultsCancelCB(self): self.hide() def screenDefaultsOKCB(self): - self.hide() + self.hide() \ No newline at end of file diff --git a/gui/dialog/user_screen.py b/gui/dialog/user_screen.py index 7322efcc..f77be168 100644 --- a/gui/dialog/user_screen.py +++ b/gui/dialog/user_screen.py @@ -197,22 +197,24 @@ def __init__(self, parent: "ControlMain"): self.setLayout(vBoxColParams1) def setSlit1XCB(self): - self.parent.send_to_server("setSlit1X", [self.slit1XMotor_ledit.text()]) + comm_s = "setSlit1X(" + str(self.slit1XMotor_ledit.text()) + ")" + self.parent.send_to_server(comm_s) def setSlit1YCB(self): - self.parent.send_to_server("setSlit1Y", [self.slit1YMotor_ledit.text()]) + comm_s = "setSlit1Y(" + str(self.slit1YMotor_ledit.text()) + ")" + self.parent.send_to_server(comm_s) def unmountColdCB(self): - self.parent.send_to_server("unmountCold") + self.parent.send_to_server("unmountCold()") def testRobotCB(self): - self.parent.send_to_server("testRobot") + self.parent.send_to_server("testRobot()") def recoverRobotCB(self): - self.parent.send_to_server("recoverRobot") + self.parent.send_to_server("recoverRobot()") def dryGripperCB(self): - self.parent.send_to_server("dryGripper") + self.parent.send_to_server("dryGripper()") def stopDetCB(self): logger.info("stopping detector") @@ -233,16 +235,16 @@ def rebootZebraIOC_CB(self): self.parent.rebootZebraIOC_pv.put(1) def SEgovCB(self): - self.parent.send_to_server("setGovState", ["SE"]) + self.parent.send_to_server("setGovRobot(gov_robot, 'SE')") def SAgovCB(self): - self.parent.send_to_server("setGovState", ["SA"]) + self.parent.send_to_server("setGovRobot(gov_robot, 'SA')") def DAgovCB(self): - self.parent.send_to_server("setGovState", ["DA"]) + self.parent.send_to_server("setGovRobot(gov_robot, 'DA')") def BLgovCB(self): - self.parent.send_to_server("setGovState", ["BL"]) + self.parent.send_to_server("setGovRobot(gov_robot, 'BL')") def userScreenOKCB(self): self.hide() @@ -251,4 +253,4 @@ def screenDefaultsCancelCB(self): self.done(QtWidgets.QDialog.Rejected) def screenDefaultsOKCB(self): - self.done(QtWidgets.QDialog.Accepted) + self.done(QtWidgets.QDialog.Accepted) \ No newline at end of file diff --git a/ispybLib.py b/ispybLib.py index 842cd3df..4793b057 100644 --- a/ispybLib.py +++ b/ispybLib.py @@ -9,7 +9,6 @@ import det_lib import time import mysql.connector -from PIL import Image import logging logger = logging.getLogger(__name__) @@ -212,8 +211,6 @@ def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None sample = request['sample'] # this needs to be created and linked to a DC group if (resultType == 'fastDP'): mx_data_reduction_dict = xml_file_to_dict(xmlFileName) - comm = mx_data_reduction_dict['AutoProcProgramContainer']['AutoProcProgram']['processingCommandLine'] - mx_data_reduction_dict['AutoProcProgramContainer']['AutoProcProgram']['processingCommandLine'] = comm[len(comm)-255:] (app_id, ap_id, scaling_id, integration_id) = mx_data_reduction_to_ispyb(mx_data_reduction_dict, dc_id, mxprocessing) mxprocessing.upsert_program_ex(program_id=app_id,status=1) @@ -230,13 +227,10 @@ def insertResult(result,resultType,request,visitName,dc_id=None,xmlFileName=None daq_utils.take_crystal_picture(filename=jpegImagePrefix) jpegImageFilename = jpegImagePrefix+".jpg" jpegImageThumbFilename = jpegImagePrefix+"t.jpg" - resizeRatio = 0.4 - logger.info(f'resizing image: ratio: {resizeRatio} filename: {jpegImageThumbFilename}') - fullSnapshot = Image.open(jpegImageFilename) - resizeWidth = fullSnapshot.width * resizeRatio - resizeHeight = fullSnapshot.height * resizeRatio - thumbSnapshot = fullSnapshot.resize((int(resizeWidth), int(resizeHeight))) - thumbSnapshot.save(jpegImageThumbFilename) + node = db_lib.getBeamlineConfigParam(beamline,"adxvNode") + comm_s = f"ssh -q {node} \"{os.environ['MXPROCESSINGSCRIPTSDIR']}resize.sh {jpegImageFilename} {jpegImageThumbFilename} 40% \"&" + logger.info('resizing image: %s' % comm_s) + os.system(comm_s) seqNum = int(det_lib.detector_get_seqnum()) node = db_lib.getBeamlineConfigParam(beamline,"adxvNode") @@ -419,4 +413,4 @@ def insertRasterResult(request,visitName): params = mxacquisition.get_data_collection_group_params() params['parentid'] = sessionid params['experimenttype'] = 'OSC' - return createDataCollection(directory, filePrefix, jpegImageFilename, params, request_obj, sessionid) + return createDataCollection(directory, filePrefix, jpegImageFilename, params, request_obj, sessionid) \ No newline at end of file diff --git a/runFastDPH5.py b/runFastDPH5.py index 66c845a0..5ba58592 100755 --- a/runFastDPH5.py +++ b/runFastDPH5.py @@ -31,7 +31,7 @@ node = sys.argv[5] runDimple = int(sys.argv[6]) dimpleNode = sys.argv[7] -ispybDCID = 1 #int(sys.argv[8]) +ispybDCID = int(sys.argv[8]) comm_s = f"ssh -q {node} \"{os.environ['MXPROCESSINGSCRIPTSDIR']}fast_dp.sh {request_id} {numstart}\"" logger.info(comm_s) @@ -54,4 +54,4 @@ dimpleComm = getBlConfig("dimpleComm") comm_s = f"ssh -q {dimpleNode} \"{os.environ['MXPROCESSINGSCRIPTSDIR']}dimple.sh {request_id} {numstart}\"" logger.info(f"running dimple: {comm_s}") - os.system(comm_s) + os.system(comm_s) \ No newline at end of file