diff --git a/config_params.py b/config_params.py index 5453b6da..4eec0140 100644 --- a/config_params.py +++ b/config_params.py @@ -134,4 +134,25 @@ class OnMountAvailOptions(Enum): "S": ["V0", "H0"], "L": ["V1", "H1"] } + + +class MountState(Enum): + CURRENTLY_MOUNTING = "cm" + FAILED_MOUNTING = "fm" + MOUNTED = "m" + CURRENTLY_UNMOUNTING = "cu" + FAILED_UNMOUNTING = "fu" + + @classmethod + def get_text(cls, enum_value): + text_values = { + cls.CURRENTLY_MOUNTING: "\n(Currently Mounting)", + cls.FAILED_MOUNTING: "\n(Failed Mounting)", + cls.MOUNTED: "\n(Mounted)", + cls.CURRENTLY_UNMOUNTING: "\n(Currently Unmounting)", + cls.FAILED_UNMOUNTING: "\n(Failed Unmounting)" + } + return text_values.get(enum_value, "\n(Unknown State)") + OPHYD_COLLECTIONS = {"amx": False, "fmx": False, "nyx": True} + diff --git a/daq_lib.py b/daq_lib.py index af1fc149..f7d6abf8 100644 --- a/daq_lib.py +++ b/daq_lib.py @@ -261,19 +261,21 @@ def mountSample(sampID): if (sampID!=currentMountedSampleID): puckPos = mountedSampleDict["puckPos"] pinPos = mountedSampleDict["pinPos"] + # Set status as currently unmounting + set_mounted_pin_data(currentMountedSampleID, mount_state=MountState.CURRENTLY_UNMOUNTING.value) if robot_lib.unmountRobotSample(gov_robot, puckPos,pinPos,currentMountedSampleID): db_lib.deleteCompletedRequestsforSample(currentMountedSampleID) - set_field("mounted_pin","") - db_lib.beamlineInfo(daq_utils.beamline, 'mountedSample', info_dict={'puckPos':0,'pinPos':0,'sampleID':""}) (puckPos,pinPos,puckID) = db_lib.getCoordsfromSampleID(daq_utils.beamline,sampID) if (warmUpNeeded): gui_message("Warming gripper. Please stand by.") mountCounter = 0 + # About to mount next sample + set_mounted_pin_data(sampID, mount_state=MountState.CURRENTLY_MOUNTING.value, sample_pos={'puckPos':puckPos,'pinPos':pinPos}) mountStat = robot_lib.mountRobotSample(gov_robot, puckPos,pinPos,sampID,init=0,warmup=warmUpNeeded) if (warmUpNeeded): destroy_gui_message() - if (mountStat == 1): - set_field("mounted_pin",sampID) + if (mountStat == MOUNT_SUCCESSFUL): + set_mounted_pin_data(sampID, sample_pos={'puckPos':puckPos,'pinPos':pinPos}) detDist = beamline_lib.motorPosFromDescriptor("detectorDist") if (detDist != saveDetDist): if (getBlConfig("HePath") == 0): @@ -282,29 +284,34 @@ def mountSample(sampID): # Only run mount options when the robot is online and queue collect is off daq_macros.run_on_mount_option(sampID) gov_status = gov_lib.setGovRobot(gov_robot, 'SA') - elif(mountStat == 2): - return 2 + elif(mountStat == MOUNT_UNRECOVERABLE_ERROR): + clearMountedSample() + return MOUNT_UNRECOVERABLE_ERROR else: - return 0 + clearMountedSample() + return MOUNT_FAILURE else: - return 0 + set_mounted_pin_data(currentMountedSampleID, mount_state=MountState.FAILED_UNMOUNTING.value) + return UNMOUNT_FAILURE else: #desired sample is mounted, nothing to do - return 1 + return MOUNT_SUCCESSFUL else: #nothing mounted (puckPos,pinPos,puckID) = db_lib.getCoordsfromSampleID(daq_utils.beamline,sampID) + set_mounted_pin_data(sampID, mount_state=MountState.CURRENTLY_MOUNTING.value, sample_pos={'puckPos':puckPos,'pinPos':pinPos}) mountStat = robot_lib.mountRobotSample(gov_robot, puckPos,pinPos,sampID,init=1) - if (mountStat == 1): - set_field("mounted_pin",sampID) + if (mountStat == MOUNT_SUCCESSFUL): + set_mounted_pin_data(sampID, sample_pos={'puckPos':puckPos,'pinPos':pinPos}) if getBlConfig('robot_online') and getBlConfig("queueCollect") == 0: # Only run mount options when the robot is online and queue collect is off daq_macros.run_on_mount_option(sampID) gov_status = gov_lib.setGovRobot(gov_robot, 'SA') - elif(mountStat == 2): - return 2 + elif(mountStat == MOUNT_UNRECOVERABLE_ERROR): + clearMountedSample() + return MOUNT_UNRECOVERABLE_ERROR else: - return 0 - db_lib.beamlineInfo(daq_utils.beamline, 'mountedSample', info_dict={'puckPos':puckPos,'pinPos':pinPos,'sampleID':sampID}) - return 1 + clearMountedSample() + return MOUNT_FAILURE + return MOUNT_SUCCESSFUL def clearMountedSample(): @@ -312,6 +319,17 @@ def clearMountedSample(): db_lib.beamlineInfo(daq_utils.beamline, 'mountedSample', info_dict={'puckPos':0,'pinPos':0,'sampleID':""}) +def set_mounted_pin_data(sample_id, mount_state=MountState.MOUNTED.value, sample_pos=None): + current_pin_data = f"{sample_id},{mount_state}" + logger.info(f"current pin data = {current_pin_data}") + set_field("mounted_pin", current_pin_data) + + if sample_pos: + info_dict = { 'puckPos' : sample_pos["puckPos"], + 'pinPos' : sample_pos["pinPos"], + 'sampleID': sample_id } + db_lib.beamlineInfo(daq_utils.beamline, 'mountedSample', info_dict=info_dict) + def unmountSample(): global mountCounter @@ -322,13 +340,15 @@ def unmountSample(): if (currentMountedSampleID != ""): puckPos = mountedSampleDict["puckPos"] pinPos = mountedSampleDict["pinPos"] + # Set status as currently unmounting + set_mounted_pin_data(currentMountedSampleID, mount_state=MountState.CURRENTLY_UNMOUNTING.value) if robot_lib.unmountRobotSample(gov_robot, puckPos,pinPos,currentMountedSampleID): db_lib.deleteCompletedRequestsforSample(currentMountedSampleID) robot_lib.finish() - set_field("mounted_pin","") - db_lib.beamlineInfo(daq_utils.beamline, 'mountedSample', info_dict={'puckPos':0,'pinPos':0,'sampleID':""}) + clearMountedSample() return 1 else: + set_mounted_pin_data(currentMountedSampleID, mount_state=MountState.FAILED_UNMOUNTING.value) return 0 def unmountCold(): @@ -337,14 +357,17 @@ def unmountCold(): if (currentMountedSampleID != ""): puckPos = mountedSampleDict["puckPos"] pinPos = mountedSampleDict["pinPos"] + # Set status as currently unmounting + set_mounted_pin_data(currentMountedSampleID, mount_state=MountState.CURRENTLY_UNMOUNTING.value) if robot_lib.unmountRobotSample(gov_robot, puckPos,pinPos,currentMountedSampleID): db_lib.deleteCompletedRequestsforSample(currentMountedSampleID) - robot_lib.parkGripper() - set_field("mounted_pin","") - db_lib.beamlineInfo(daq_utils.beamline, 'mountedSample', info_dict={'puckPos':0,'pinPos':0,'sampleID':""}) + if getBlConfig("robot_online"): + robot_lib.parkGripper() + clearMountedSample() setPvDesc("robotGovActive",1) return 1 else: + set_mounted_pin_data(currentMountedSampleID, mount_state=MountState.FAILED_UNMOUNTING.value) return 0 def waitBeam(): diff --git a/gui/control_main.py b/gui/control_main.py index 481e8853..4c24b4ab 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -66,7 +66,7 @@ from gui.vector import VectorMarker, VectorWidget from QPeriodicTable import QPeriodicTable from threads import RaddoseThread, ServerCheckThread, VideoThread -from utils import validation +from utils import validation, custom_pv logger = logging.getLogger() @@ -5243,7 +5243,7 @@ def initCallbacks(self): 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.mountedPin_pv = custom_pv.MountedPinPV(daq_utils.beamlineComm + "mounted_pin") self.mountedPinSignal.connect(self.processMountedPin) self.mountedPin_pv.add_callback(self.mountedPinChangedCB) det_stop_pv = daq_utils.pvLookupDict["stopEiger"] diff --git a/gui/dewar_tree.py b/gui/dewar_tree.py index 6efd1760..bbdb9b55 100644 --- a/gui/dewar_tree.py +++ b/gui/dewar_tree.py @@ -14,6 +14,7 @@ IS_STAFF, PUCKS_PER_DEWAR_SECTOR, SAMPLE_TIMER_DELAY, + MountState ) if typing.TYPE_CHECKING: @@ -136,7 +137,7 @@ def keyPressEvent(self, event): def refreshTree(self): self.parent.dewarViewToggleCheckCB() - def set_mounted_sample(self, item): + def set_mounted_sample(self, item, sample_name=None): # Formats the text of the item that is passed in as the mounted sample item.setForeground(QtGui.QColor("red")) font = QtGui.QFont() @@ -144,6 +145,12 @@ def set_mounted_sample(self, item): font.setItalic(True) font.setOverline(True) item.setFont(font) + if self.parent.mountedPin_pv.get_pin_state() is not None: + state = self.parent.mountedPin_pv.get_pin_state() + mount_state = MountState(state) + if sample_name is None: + sample_name = item.text() + item.setText(sample_name + MountState.get_text(mount_state)) def refreshTreeDewarView(self, get_latest_pucks=False): puck = "" @@ -230,8 +237,9 @@ def add_samples_to_puck_tree( item.setData(sample_id, 32) item.setData("sample", 33) if sample_id == self.parent.mountedPin_pv.get(): - self.set_mounted_sample(item) + self.set_mounted_sample(item, position_s) parentItem.appendRow(item) + if sample_id == self.parent.mountedPin_pv.get(): mountedIndex = self.model.indexFromItem(item) # looking for the selected item @@ -261,7 +269,6 @@ def add_samples_to_puck_tree( elif mountedIndex: current_index = mountedIndex item = self.model.itemFromIndex(mountedIndex) - self.set_mounted_sample(item) elif selectedIndex: current_index = selectedIndex elif collectionRunning and mountedIndex: diff --git a/utils/custom_pv.py b/utils/custom_pv.py new file mode 100644 index 00000000..a7330a5b --- /dev/null +++ b/utils/custom_pv.py @@ -0,0 +1,14 @@ +from epics import PV + + +class MountedPinPV(PV): + + def get(self, *args, **kwargs): + value = str(super().get(*args, **kwargs)) + return value.split(",")[0] + + def get_pin_state(self): + value = str(super().get()).split(",") + if len(value) == 2: + return value[1] + return None