From 5c469a826d8cb5356b385374c895ac798a65b2dc Mon Sep 17 00:00:00 2001 From: rhfogh Date: Fri, 1 Mar 2024 12:40:36 +0000 Subject: [PATCH] Ported real centring code changes from other side branch. Works with 20240301 --- .../HardwareObjects/Gphl/GphlMessages.py | 24 +++- .../HardwareObjects/Gphl/GphlWorkflow.py | 108 ++++++++++++++++-- .../Gphl/GphlWorkflowConnection.py | 20 +++- 3 files changed, 137 insertions(+), 15 deletions(-) diff --git a/mxcubecore/HardwareObjects/Gphl/GphlMessages.py b/mxcubecore/HardwareObjects/Gphl/GphlMessages.py index 715e52dc80..a81ff53fe8 100644 --- a/mxcubecore/HardwareObjects/Gphl/GphlMessages.py +++ b/mxcubecore/HardwareObjects/Gphl/GphlMessages.py @@ -488,11 +488,21 @@ class CollectionDone(MessageData): INTENT = "EVENT" - def __init__(self, proposalId, status, procWithLatticeParams=False, imageRoot=None): + def __init__( + self, + proposalId, + status, + procWithLatticeParams=False, + imageRoot=None, + scanIdMap=None, + centrings=None, + ): self._proposalId = proposalId self._imageRoot = imageRoot self._status = status self._procWithLatticeParams = procWithLatticeParams + self._scanIdMap = scanIdMap + self._centrings = centrings @property def proposalId(self): @@ -515,6 +525,18 @@ def procWithLatticeParams(self): """Boolean, whether lattice parameters should be used for processing""" return self._procWithLatticeParams + @property + def scanIdMap(self): + """Dict[str,str] scan.id_:GoniostatTranslation.id_""" + return self._scanIdMap + + @property + def centrings(self): + """set(GoniostatTranslation) + + New GoniostatTranslations acquired during acquisition""" + return self._centrings + # Complex payloads diff --git a/mxcubecore/HardwareObjects/Gphl/GphlWorkflow.py b/mxcubecore/HardwareObjects/Gphl/GphlWorkflow.py index 5c3f50e482..83a4d7128c 100644 --- a/mxcubecore/HardwareObjects/Gphl/GphlWorkflow.py +++ b/mxcubecore/HardwareObjects/Gphl/GphlWorkflow.py @@ -199,8 +199,14 @@ def __init__(self, name): # Subprocess names to track which subprocess is getting info self._server_subprocess_names = {} - # Dictionary holding (directory, prefix, run_number, first_image) : motors_dict - self._scan_to_motors = OrderedDict() + # Dictionary holding (prefix, run_number, first_image) : scan_id + self._key_to_scan = OrderedDict() + # Dictionary of scan_id to id of actual translation used + self._scan_id_to_translation_id = {} + # Translation ID matching current centring + self._latest_translation_id = None + # GoniostatTranslations generated in recentring + self._recentrings = [] # Rotation axis role names, ordered from holder towards sample self.rotation_axis_roles = [] @@ -803,6 +809,11 @@ def execute(self): "collectOscillationStarted", HWR.beamline.collect, ) + dispatcher.connect( + self.handle_collection_end, + "collectOscillationFinished", + HWR.beamline.collect, + ) try: while True: if self._workflow_queue is None: @@ -835,6 +846,11 @@ def execute(self): "collectOscillationStarted", HWR.beamline.collect, ) + dispatcher.disconnect( + self.handle_collection_end, + "collectOscillationFinished", + HWR.beamline.collect, + ) def post_execute(self): @@ -1365,6 +1381,10 @@ def setup_data_collection(self, payload, correlation_id): """ geometric_strategy = payload + self._key_to_scan.clear() + self._scan_id_to_translation_id.clear() + self._recentrings = [] + # Set up gphl_workflow_model = self._queue_entry.get_data_model() wftype = gphl_workflow_model.wftype @@ -1572,6 +1592,8 @@ def setup_data_collection(self, payload, correlation_id): translation, current_pos_dict = self.execute_sample_centring( q_e, sweepSetting ) + self._latest_translation_id = translation.id_ + self._recentrings.append(translation) # Update current position current_okp = tuple( current_pos_dict[role] for role in self.rotation_axis_roles @@ -1579,7 +1601,6 @@ def setup_data_collection(self, payload, correlation_id): current_xyz = tuple( current_pos_dict[role] for role in self.translation_axis_roles ) - goniostatTranslations.append(translation) gphl_workflow_model.current_rotation_id = sweepSetting.id_ elif gphl_workflow_model.characterisation_done or wftype == "diffractcal": @@ -1613,7 +1634,8 @@ def setup_data_collection(self, payload, correlation_id): translation = GphlMessages.GoniostatTranslation( rotation=sweepSetting, **translation_settings ) - goniostatTranslations.append(translation) + self._latest_translation_id = translation.id_ + self._recentrings.append(translation) gphl_workflow_model.current_rotation_id = sweepSetting.id_ else: @@ -1624,13 +1646,14 @@ def setup_data_collection(self, payload, correlation_id): translation = GphlMessages.GoniostatTranslation( rotation=sweepSetting, **translation_settings ) - goniostatTranslations.append(translation) + self._latest_translation_id = None else: if has_recentring_file: settings.update(translation_settings) q_e = self.enqueue_sample_centring(motor_settings=settings) translation, dummy = self.execute_sample_centring(q_e, sweepSetting) - goniostatTranslations.append(translation) + self._latest_translation_id = translation.id_ + self._recentrings.append(translation) gphl_workflow_model.current_rotation_id = sweepSetting.id_ if recentring_mode == "start": # We want snapshots in this mode, @@ -1656,8 +1679,10 @@ def setup_data_collection(self, payload, correlation_id): requestedRotationId=sweepSetting.id_, **translation_settings ) - goniostatTranslations.append(translation) + self._latest_translation_id = translation.id_ + self._recentrings.append(translation) gphl_workflow_model.current_rotation_id = newRotation.id_ + goniostatTranslations.append(translation) # calculate or determine centring for remaining sweeps for sweepSetting in sweepSettings[1:]: @@ -1913,6 +1938,13 @@ def collect_data(self, payload, correlation_id): path_template.start_num = acq_parameters.first_image path_template.num_files = acq_parameters.num_images + key = ( + path_template.base_prefix, + path_template.run_number, + path_template.start_num + ) + self._key_to_scan[key] = scan + # Handle orientations and (re) centring goniostatRotation = sweep.goniostatSweepSetting rotation_id = goniostatRotation.id_ @@ -2010,10 +2042,14 @@ def collect_data(self, payload, correlation_id): else: status = 0 + failedScanIds = set(scan.id_ for scan in self._key_to_scan.values()) + return GphlMessages.CollectionDone( status=status, proposalId=collection_proposal.id_, procWithLatticeParams=gphl_workflow_model.use_cell_for_processing, + scanIdMap=self._scan_id_to_translation_id, + centrings=set(self._recentrings), ) def select_lattice(self, payload, correlation_id): @@ -2457,6 +2493,22 @@ def obtain_prior_information(self, payload, correlation_id): return priorInformation + def handle_collection_end( + self, dummy1, dummy2, dummy3, dummy4, dummy5, collect_dict + ): + """ Read and process collectOscillationFinished signal + which means scan finished successfully""" + key = ( + collect_dict["fileinfo"].get("prefix"), + collect_dict["fileinfo"].get("run_number"), + collect_dict["oscillation_sequence"][0].get("start_image_number") + ) + scan = self._key_to_scan.pop(key, None) + if scan is None: + raise RuntimeError( + "No scan matching prefix: %s, run_number: %s, start_image_number: %s at end" + % key + ) def handle_collection_start( self, owner, blsampleid, barcode, location, collect_dict, osc_id ): @@ -2465,14 +2517,46 @@ def handle_collection_start( NB only collect_dict is reliably non-null""" key = ( - collect_dict["fileinfo"].get("directory"), collect_dict["fileinfo"].get("prefix"), collect_dict["fileinfo"].get("run_number"), - collect_dict["oscillation_sequence"].get("start_image_number") + collect_dict["oscillation_sequence"][0].get("start_image_number") + ) + scan = self._key_to_scan.get(key) + if scan is None: + raise RuntimeError( + "No scan matching prefix: %s, run_number: %s, start_image_number: %s at start" + % key + ) + + translation_settings = dict( + (role, collect_dict["motors"].get(role)) + for role in self.translation_axis_roles ) - if key in self._scan_to_motors: - raise RuntimeError("Duplicate scan found: " + str(key)) - self._scan_to_motors[key] = collect_dict["motors"].copy() + if not self._scan_id_to_translation_id or None in translation_settings.values(): + # First sweep or not first scan in sweep + # No new centring done + if self._latest_translation_id: + self._scan_id_to_translation_id[scan.id_] = self._latest_translation_id + else: + # NBNB RECHECK!!! + # We must be in centring mode None: No real centring known, use calculated + self._scan_id_to_translation_id[scan.id_] = None + else: + # First scan in sweep (not first sweep) + # We have recentred. Make new translation object + translation_settings = dict( + (role, HWR.beamline.diffractometer.get_motor_positions().get(role)) + for role in self.translation_axis_roles + ) + translation = GphlMessages.GoniostatTranslation( + requestedRotationId=scan.sweep.goniostatSweepSetting.id_, + **translation_settings + ) + self._latest_translation_id = translation.id_ + self._scan_id_to_translation_id[scan.id_] = translation.id_ + self._recentrings.append(translation) + + # Utility functions diff --git a/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py b/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py index 9b1f0d243b..4e13dfc6c8 100644 --- a/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py +++ b/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py @@ -800,7 +800,6 @@ def _GoniostatRotation_to_python(self, py4jGoniostatRotation, isSweepSetting=Fal result = GphlMessages.GoniostatRotation( id_=uuid.UUID(uuidString), **axisSettings ) - py4jGoniostatTranslation = py4jGoniostatRotation.getTranslation() if py4jGoniostatTranslation: translationAxisSettings = py4jGoniostatTranslation.getAxisSettings() @@ -1046,8 +1045,25 @@ def _CollectionDone_to_java(self, collectionDone): proposalId = jvm.java.util.UUID.fromString( conversion.text_type(collectionDone.proposalId) ) + centrings = set( + self._GoniostatTranslation_to_java(translation) + for translation in collectionDone.centrings + ) + scanIdMap = {} + for item in collectionDone.scanIdMap.items(): + scanIdMap[ + jvm.java.util.UUID.fromString( + conversion.text_type(item[0]) + ) + ] = jvm.java.util.UUID.fromString( + conversion.text_type(item[1]) + ) return jvm.astra.messagebus.messages.information.CollectionDoneImpl( - proposalId, collectionDone.imageRoot, collectionDone.status + proposalId, + collectionDone.status, + collectionDone.procWithLatticeParams, + scanIdMap, + centrings, ) def _SelectedLattice_to_java(self, selectedLattice):