diff --git a/docs/source/dev/json-schema-generated-user-interface.md b/docs/source/dev/json-schema-generated-user-interface.md index 58ec4b65fd..cb4a29a9df 100644 --- a/docs/source/dev/json-schema-generated-user-interface.md +++ b/docs/source/dev/json-schema-generated-user-interface.md @@ -9,7 +9,7 @@ We have a system for code-generated interfaces that can be plugged in to both a Qt and a Web user interface. It is implemented for Qt5, and (as of 20240213) just about ready for testing with the web interface. -The system is used to generate parameter queries from mxcubecore (for now from the GΦL workflow). It includes a wide range of widgets; multiple nested layout fields; support for custom update functions triggered when values are edited, that can reset values of other fields, change widget colouring and pulldown enums; field value validation; and colouring and disabling of action for invalid values. There are several examples of pulldown menu contents being modified depending on the values of other fields. The system is started with a `PARAMETERS_NEEDED` on signals being sent from the mxcubecore side with the JSON schemas as parameters. The UI side responds by sending a `PARAMETER_RETURN_SIGNAL` with the complete dictionary of parameter values. There is also a control parameter, that determines whether the response matches a Continue or Cancel click (closing the UI popup), or whether it is a request for a UI update. In the latter case the update is sent from the mxcubecore side with a `PARAMETER_UPDATE_SIGNAL`. +The system is used to generate parameter queries from mxcubecore (for now from the GPhL workflow). It includes a wide range of widgets; multiple nested layout fields; support for custom update functions triggered when values are edited, that can reset values of other fields, change widget colouring and pulldown enums; field value validation; and colouring and disabling of action for invalid values. There are several examples of pulldown menu contents being modified depending on the values of other fields. The system is started with a `PARAMETERS_NEEDED` on signals being sent from the mxcubecore side with the JSON schemas as parameters. The UI side responds by sending a `PARAMETER_RETURN_SIGNAL` with the complete dictionary of parameter values. There is also a control parameter, that determines whether the response matches a Continue or Cancel click (closing the UI popup), or whether it is a request for a UI update. In the latter case the update is sent from the mxcubecore side with a `PARAMETER_UPDATE_SIGNAL`. The form of the JSON schemas used has been agreed between Rasmus Fogh, Jean-Baptiste Florial, and Marcus Oscarsson. Hopefully it fits with standard practices. diff --git a/mxcubecore/HardwareObjects/ALBA/ALBAMiniDiff.py b/mxcubecore/HardwareObjects/ALBA/ALBAMiniDiff.py index 6d38cf6442..dc4b19341b 100644 --- a/mxcubecore/HardwareObjects/ALBA/ALBAMiniDiff.py +++ b/mxcubecore/HardwareObjects/ALBA/ALBAMiniDiff.py @@ -281,6 +281,7 @@ def use_sample_changer(self): return True # TODO:Implement dynamically + @property def in_plate_mode(self): """ Overrides GenericDiffracometer method. @@ -349,7 +350,7 @@ def prepare_centring(self): # {"X": (x - self.zoom_centre['x'])/ self.pixels_per_mm_x, # "Y": (y - self.zoom_centre['y'])/ self.pixels_per_mm_y}) - # if self.in_plate_mode(): + # if self.in_plate_mode: # dynamic_limits = self.phi_motor_hwobj.get_dynamic_limits() # if click == 0: # self.phi_motor_hwobj.set_value(dynamic_limits[0]) diff --git a/mxcubecore/HardwareObjects/ALBA/XalocMiniDiff.py b/mxcubecore/HardwareObjects/ALBA/XalocMiniDiff.py index 5c0ac8c9c4..a8034e3008 100644 --- a/mxcubecore/HardwareObjects/ALBA/XalocMiniDiff.py +++ b/mxcubecore/HardwareObjects/ALBA/XalocMiniDiff.py @@ -125,6 +125,7 @@ def getBeamInfo(self, update_beam_callback): update_beam_callback(data) + @property def in_plate_mode(self): return False @@ -146,7 +147,7 @@ def manual_centring(self): } ) - if self.in_plate_mode(): + if self.in_plate_mode: dynamic_limits = self.phi_motor_hwobj.get_dynamic_limits() if click == 0: self.phi_motor_hwobj.set_value(dynamic_limits[0]) diff --git a/mxcubecore/HardwareObjects/Beamline.py b/mxcubecore/HardwareObjects/Beamline.py index e209b6e77d..03d12648de 100644 --- a/mxcubecore/HardwareObjects/Beamline.py +++ b/mxcubecore/HardwareObjects/Beamline.py @@ -396,7 +396,7 @@ def get_default_acquisition_parameters(self, acquisition_type="default"): acq_parameters.detector_binning_mode = "" try: - acq_parameters.detector_roi_mode = self.detector.get_roi_mode() + acq_parameters.detector_roi_mode = self.detector.get_roi_mode_name() except Exception: self.log.warning( "get_default_acquisition_parameters: " diff --git a/mxcubecore/HardwareObjects/DESY/P11Collect.py b/mxcubecore/HardwareObjects/DESY/P11Collect.py index 487ac195cb..4ffd521f85 100644 --- a/mxcubecore/HardwareObjects/DESY/P11Collect.py +++ b/mxcubecore/HardwareObjects/DESY/P11Collect.py @@ -1264,5 +1264,5 @@ def take_crystal_snapshots(self): self._take_crystal_snapshot(snapshot_filename) time.sleep(1) # needed, otherwise will get the same images if number_of_snapshots > 1: - HWR.beamline.diffractometer.move_omega_relative(90) + HWR.beamline.diffractometer.omega.set_value_relative(90) time.sleep(1) # needed, otherwise will get the same images diff --git a/mxcubecore/HardwareObjects/DESY/P11NanoDiff.py b/mxcubecore/HardwareObjects/DESY/P11NanoDiff.py index a58963aba7..1db25859ac 100644 --- a/mxcubecore/HardwareObjects/DESY/P11NanoDiff.py +++ b/mxcubecore/HardwareObjects/DESY/P11NanoDiff.py @@ -338,7 +338,7 @@ def centring_done(self, centring_procedure, motor_p=None): # if 3 click centring move -180. well. dont, in principle the calculated # centred positions include omega to initial position pass - # if not self.in_plate_mode(): + # if not self.in_plate_mode: # self.log.debug("Centring finished. Moving omega back to initial position") # self.motor_hwobj_dict['phi'].set_value_relative(-180, timeout=None) # self.log.debug(" Moving omega done") diff --git a/mxcubecore/HardwareObjects/EMBL/EMBLDoorInterlock.py b/mxcubecore/HardwareObjects/EMBL/EMBLDoorInterlock.py index ce66f4c9c9..0ed36fc63a 100644 --- a/mxcubecore/HardwareObjects/EMBL/EMBLDoorInterlock.py +++ b/mxcubecore/HardwareObjects/EMBL/EMBLDoorInterlock.py @@ -149,7 +149,7 @@ def unlock_door_interlock(self): """ if HWR.beamline.diffractometer is not None: detector_distance = HWR.beamline.detector.distance - if HWR.beamline.diffractometer.in_plate_mode(): + if HWR.beamline.diffractometer.in_plate_mode: if detector_distance is not None: if detector_distance.get_value() < 780: detector_distance.set_value(800, timeout=None) diff --git a/mxcubecore/HardwareObjects/EMBL/EMBLMiniDiff.py b/mxcubecore/HardwareObjects/EMBL/EMBLMiniDiff.py index 28f65c3f9a..7d4c3ba390 100644 --- a/mxcubecore/HardwareObjects/EMBL/EMBLMiniDiff.py +++ b/mxcubecore/HardwareObjects/EMBL/EMBLMiniDiff.py @@ -178,7 +178,7 @@ def use_sample_changer(self): :return: bool """ - return not self.in_plate_mode() + return not self.in_plate_mode def beam_position_changed(self, value): self.beam_position = value @@ -301,7 +301,7 @@ def set_phase(self, phase, timeout=80): "Diffractometer: Setting %s phase. Please wait..." % phase ) - if self.in_plate_mode() and ( + if self.in_plate_mode and ( phase in (GenericDiffractometer.PHASE_TRANSFER, GenericDiffractometer.PHASE_BEAM) or self.current_phase @@ -391,7 +391,7 @@ def manual_centring(self): "Y": (y - self.beam_position[1]) / self.pixels_per_mm_y, } ) - if self.in_plate_mode(): + if self.in_plate_mode: dynamic_limits = self.get_osc_limits() if click == 0: self.motor_hwobj_dict["phi"].set_value(dynamic_limits[0] + 0.5) @@ -601,7 +601,7 @@ def convert_from_obj_to_name(self, motor_pos): return motors def visual_align(self, point_1, point_2): - if self.in_plate_mode(): + if self.in_plate_mode: self.log.info("EMBLMiniDiff: Visual align not available in Plate mode") else: t1 = [point_1.sampx, point_1.sampy, point_1.phiy] diff --git a/mxcubecore/HardwareObjects/GenericDiffractometer.py b/mxcubecore/HardwareObjects/GenericDiffractometer.py index 4eaa3fb6ef..5d85858588 100644 --- a/mxcubecore/HardwareObjects/GenericDiffractometer.py +++ b/mxcubecore/HardwareObjects/GenericDiffractometer.py @@ -303,7 +303,6 @@ def __init__(self, name): self.fast_shutter_is_open = None self.centring_status = {"valid": False} self.centring_time = 0 - self.user_confirms_centring = None self.user_clicked_event = None self.automatic_centring_try_count = 1 self.omega_reference_par = None @@ -327,7 +326,6 @@ def init(self): # Internal values ----------------------------------------------------- self.ready_event = gevent.event.Event() self.user_clicked_event = gevent.event.AsyncResult() - self.user_confirms_centring = True self.beamstop = self.get_object_by_role("beamstop") self.aperture = self.get_object_by_role("aperture") @@ -682,6 +680,7 @@ def execute_server_task(self, method, timeout=30, *args): self.wait_device_ready(timeout) self.ready_event.set() + @property def in_plate_mode(self): """Returns True if diffractometer in plate mod @@ -689,13 +688,6 @@ def in_plate_mode(self): """ return self.head_type == GenericDiffractometer.HEAD_TYPE_PLATE - def get_head_type(self): - """Returns head type - - :returns: string - """ - return self.head_type - def use_sample_changer(self): """Returns True if sample changer is in use @@ -1000,7 +992,7 @@ def centring_done(self, centring_procedure): # if 3 click centring move -180. well. dont, in principle the calculated # centred positions include omega to initial position pass - # if not self.in_plate_mode(): + # if not self.in_plate_mode: # self.log.debug("Centring finished. Moving omega back to initial position") # self.motor_hwobj_dict['phi'].set_value_relative(-180, timeout=None) # self.log.debug(" Moving omega done") diff --git a/mxcubecore/HardwareObjects/Gphl/CollectEmulator.py b/mxcubecore/HardwareObjects/Gphl/CollectEmulator.py index 27e1b97a1b..1df6280121 100644 --- a/mxcubecore/HardwareObjects/Gphl/CollectEmulator.py +++ b/mxcubecore/HardwareObjects/Gphl/CollectEmulator.py @@ -236,10 +236,10 @@ def data_collection_hook(self): data_collect_parameters = self.current_dc_parameters if not HWR.beamline.gphl_workflow: - raise ValueError("Emulator requires GΦL workflow installation") + raise ValueError("Emulator requires GPhL workflow installation") gphl_connection = HWR.beamline.gphl_connection if not gphl_connection: - raise ValueError("Emulator requires GΦL connection installation") + raise ValueError("Emulator requires GPhL connection installation") # Get program locations simcal_executive = gphl_connection.get_executable("simcal") @@ -326,7 +326,7 @@ def set_ulimit(): # NBNB TODO put in time-out, somehow return_code = running_process.wait() except Exception: - self.log.error("Error in GΦL collection emulation") + self.log.error("Error in GPhL collection emulation") raise finally: fp1.close() diff --git a/mxcubecore/HardwareObjects/Gphl/GphlQueueEntry.py b/mxcubecore/HardwareObjects/Gphl/GphlQueueEntry.py index ed16d94325..80a3525e80 100644 --- a/mxcubecore/HardwareObjects/Gphl/GphlQueueEntry.py +++ b/mxcubecore/HardwareObjects/Gphl/GphlQueueEntry.py @@ -25,6 +25,7 @@ from mxcubecore import HardwareRepository as HWR from mxcubecore.queue_entry.base_queue_entry import BaseQueueEntry +from mxcubecore.utils import mxutils __credits__ = ["MXCuBE collaboration"] __license__ = "LGPLv3+" @@ -35,7 +36,7 @@ class GphlWorkflowQueueEntry(BaseQueueEntry): def execute(self): BaseQueueEntry.execute(self) - msg = "Starting GΦL workflow (%s), please wait." % ( + msg = "Starting GPhL workflow (%s), please wait." % ( self.get_data_model().strategy_name ) logging.getLogger("user_level_log").info(msg) @@ -45,20 +46,59 @@ def pre_execute(self): BaseQueueEntry.pre_execute(self) if not HWR.beamline.gphl_workflow.is_ready(): logging.getLogger("user_level_log").warning( - "WARNING: GΦL workflow was not ready - cleaning up" + "WARNING: GPhL workflow was not ready - cleaning up" ) HWR.beamline.gphl_workflow.post_execute() HWR.beamline.gphl_workflow.pre_execute(self) logging.getLogger("HWR").debug("Done GphlWorkflowQueueEntry.pre_execute") def post_execute(self): + self.finalize_mxlims() BaseQueueEntry.post_execute(self) - msg = "Finishing GΦL workflow (%s)" % (self.get_data_model().strategy_name) + msg = "Finishing GPhL workflow (%s)" % (self.get_data_model().strategy_name) logging.getLogger("user_level_log").info(msg) HWR.beamline.gphl_workflow.post_execute() def stop(self): HWR.beamline.gphl_workflow.workflow_aborted("Dummy", "Dummy") BaseQueueEntry.stop(self) - logging.getLogger("user_level_log").info("MXCuBE aborting current GΦL workflow") + logging.getLogger("user_level_log").info( + "MXCuBE aborting current GPhL workflow" + ) self.get_view().setText(1, "Stopped") + + def init_mxlims(self): + """Initialise MXLIMS MxExperimentMessage if it is not already set""" + + if self.get_mxlims_job() is None: + data_model = self.get_data_model() + self._mxlims_job, mxlims_sample = mxutils.make_mx_experiment( + sample=data_model.get_sample_node(), + tracking_data=data_model.tracking_data, + measured_flux=HWR.beamline.flux.get_value(), + ) + def finalize_mxlims(self): + """Finalize MXLIMS MxExperimentMessage setting information at end of execution + """ + mx_experiment = self.get_mxlims_job() + if not mx_experiment: + # Only happens if there was an error upstream anyway + return + data_model = self.get_data_model() + workflow_name = data_model.workflow_name + if not mx_experiment.experiment_strategy: + mx_experiment.experiment_strategy = workflow_name + mx_experiment.radiation_dose = data_model.total_radiation_dose + mx_experiment.selected_space_group_name = data_model.space_group + cell_parameters = data_model.cell_parameters + if cell_parameters: + unit_cell = mxutils.make_unit_cell(*cell_parameters) + if unit_cell: + mx_experiment.selected_unit_cell = unit_cell + extensions = mx_experiment.extensions + if not extensions: + extensions = mx_experiment.extensions = {} + gphl_extensions = extensions.setdefault(data_model.GPHL_WORKFLOW_EXTENSION, {}) + gphl_extensions.update(data_model.strategy_options) + gphl_extensions["experiment_strategy"] = workflow_name + diff --git a/mxcubecore/HardwareObjects/Gphl/GphlWorkflow.py b/mxcubecore/HardwareObjects/Gphl/GphlWorkflow.py index 9348c3440f..0bc4d679b6 100644 --- a/mxcubecore/HardwareObjects/Gphl/GphlWorkflow.py +++ b/mxcubecore/HardwareObjects/Gphl/GphlWorkflow.py @@ -44,6 +44,7 @@ import gevent import gevent.event import gevent.queue +import numpy from mxcubecore import HardwareRepository as HWR from mxcubecore.BaseHardwareObjects import ( @@ -268,6 +269,7 @@ def init(self): "WorkflowAborted": self.workflow_aborted, "WorkflowCompleted": self.workflow_completed, "WorkflowFailed": self.workflow_failed, + "StartEnactment": self.start_enactment, } # Set standard configurable file paths @@ -425,7 +427,7 @@ def query_pre_strategy_params(self, choose_lattice=None): lattice_tags = [""] + list(lattice2point_group_tags) point_groups = [""] + all_point_group_tags schema = { - "title": "GΦL Pre-strategy parameters", + "title": "GPhL Pre-strategy parameters", "type": "object", "properties": {}, } @@ -563,6 +565,8 @@ def query_pre_strategy_params(self, choose_lattice=None): energy_tag = ll0[0] else: energy_tag = self.config.settings["default_beam_energy_tag"] + + fields["use_cell_for_processing"]["title"] = "Use for processing" else: # Characterisation strategies = self.config.settings["characterisation_strategies"] @@ -765,7 +769,6 @@ def query_pre_strategy_params(self, choose_lattice=None): dispatcher.connect( self.receive_pre_strategy_data, self.PARAMETER_RETURN_SIGNAL, - dispatcher.Any, ) responses = dispatcher.send( self.PARAMETERS_NEEDED, @@ -788,7 +791,6 @@ def query_pre_strategy_params(self, choose_lattice=None): dispatcher.disconnect( self.receive_pre_strategy_data, self.PARAMETER_RETURN_SIGNAL, - dispatcher.Any, ) self._return_parameters = None @@ -903,6 +905,32 @@ def pre_execute(self, queue_entry): self._workflow_queue = gevent.queue.Queue() + def start_enactment(self, enactment_id: str, correlation_id: str): + """Set enactment_id and initialise MXLIMS MxExperimentMessage""" + data_model = self._queue_entry.get_data_model() + tracking_data = data_model.tracking_data + workflow_parameters = data_model.workflow_parameters + tracking_data.uuid = enactment_id + tracking_data.workflow_uid = ( + workflow_parameters.get("workflow_uid") or enactment_id + ) + # NB if it is not set it will be overwritten later + tracking_data.workflow_name = workflow_parameters.get("workflow_name") + tracking_data.workflow_type = ( + workflow_parameters.get("workflow_type") or data_model.strategy_type + ) + tracking_data.location_id = workflow_parameters.get( + "workflow_position_id" + ) or str(uuid.uuid1()) + # NB first orientation only: + tracking_data.orientation_id = workflow_parameters.get( + "workflow_kappa_settings_id" + ) + tracking_data.characterisation_id = workflow_parameters.get( + "characterisation_id" + ) + self._queue_entry.init_mxlims() + def execute(self): if self._workflow_queue is None: return @@ -938,14 +966,14 @@ def execute(self): tt0 = self._workflow_queue.get() if tt0 is StopIteration: - self.log.debug("GΦL queue StopIteration") + self.log.debug("GPhL queue StopIteration") break message_type, payload, correlation_id, result_list = tt0 func = self._processor_functions.get(message_type) if func is None: self.log.error( - "GΦL message %s not recognised by MXCuBE. Terminating...", + "GPhL message %s not recognised by MXCuBE. Terminating...", message_type, ) break @@ -953,7 +981,7 @@ def execute(self): if not self.config.settings.get("suppress_external_log_output"): func(payload, correlation_id) else: - self.log.info("GΦL queue processing %s", message_type) + self.log.info("GPhL queue processing %s", message_type) response = func(payload, correlation_id) if result_list is not None: result_list.append((response, correlation_id)) @@ -998,18 +1026,18 @@ def _add_to_queue(self, parent_model_obj, child_model_obj): # Message handlers: def workflow_aborted(self, payload=None, correlation_id=None): - logging.getLogger("user_level_log").warning("GΦL Workflow aborted.") + logging.getLogger("user_level_log").warning("GPhL Workflow aborted.") self.update_specific_state(self.SPECIFIC_STATES.ABORTED) if self._workflow_queue: self._workflow_queue.put_nowait(StopIteration) def workflow_completed(self, payload=None, correlation_id=None): - logging.getLogger("user_level_log").info("GΦL Workflow completed.") + logging.getLogger("user_level_log").info("GPhL Workflow completed.") self.update_specific_state(self.SPECIFIC_STATES.COMPLETED) self._workflow_queue.put_nowait(StopIteration) def workflow_failed(self, payload=None, correlation_id=None): - logging.getLogger("user_level_log").warning("GΦL Workflow failed.") + logging.getLogger("user_level_log").warning("GPhL Workflow failed.") self.update_specific_state(self.SPECIFIC_STATES.FAULT) self._workflow_queue.put_nowait(StopIteration) @@ -1055,9 +1083,11 @@ def query_collection_strategy(self, geometric_strategy): ) grouped_sweeps = [] + last = {} inverse_beam = False for sweep in geometric_strategy.get_ordered_sweeps(): - last = grouped_sweeps and grouped_sweeps[-1] + if grouped_sweeps: + last = grouped_sweeps[-1] if last: if sweep.sweepGroup == last["group_no"]: inverse_beam = True @@ -1123,7 +1153,7 @@ def query_collection_strategy(self, geometric_strategy): else: # Characterisation title_string = "Characterisation" - info_title = "--- GΦL Characterisation strategy ---" + info_title = "--- GPhL Characterisation strategy ---" lines = ["Experiment length: %6.1f°" % data_model.strategy_length] beam_energies = OrderedDict((("Characterisation", initial_energy),)) dose_label = "Characterisation dose (MGy)" @@ -1208,7 +1238,7 @@ def query_collection_strategy(self, geometric_strategy): reslimits = (0.5, 5.0) schema = { - "title": "GΦL %s parameters" % title_string, + "title": "GPhL %s parameters" % title_string, "type": "object", "properties": {}, } @@ -1434,7 +1464,6 @@ def query_collection_strategy(self, geometric_strategy): dispatcher.connect( self.receive_pre_collection_data, self.PARAMETER_RETURN_SIGNAL, - dispatcher.Any, ) responses = dispatcher.send( self.PARAMETERS_NEEDED, @@ -1454,7 +1483,6 @@ def query_collection_strategy(self, geometric_strategy): dispatcher.disconnect( self.receive_pre_collection_data, self.PARAMETER_RETURN_SIGNAL, - dispatcher.Any, ) self._return_parameters = None @@ -1678,11 +1706,11 @@ def setup_data_collection(self, payload, correlation_id): # Enqueue data collection if gphl_workflow_model.characterisation_done: # Data collection TODO: Use workflow info to distinguish - new_dcg_name = "GΦL Data Collection" + new_dcg_name = "GPhL Data Collection" elif wftype == "diffractcal": - new_dcg_name = "GΦL DiffractCal" + new_dcg_name = "GPhL DiffractCal" else: - new_dcg_name = "GΦL Characterisation" + new_dcg_name = "GPhL Characterisation" self.log.debug("setup_data_collection %s", new_dcg_name) new_dcg_model = queue_model_objects.TaskGroup() new_dcg_model.set_enabled(True) @@ -2017,7 +2045,8 @@ def collect_data(self, payload, correlation_id): last_orientation = () maxdev = -1 snapshotted_rotation_ids = set() - scan_numbers = {} + characterisation_id = None + # scan_numbers = {} for scan in scans: sweep = scan.sweep acq = queue_model_objects.Acquisition() @@ -2097,16 +2126,23 @@ def collect_data(self, payload, correlation_id): path_template.run_number = int(ss0) if ss0 else 1 path_template.start_num = acq_parameters.first_image path_template.num_files = acq_parameters.num_images - if ( - path_template.suffix.endswith("h5") - and gphl_workflow_model.characterisation_done - and len(sweep.scans) > 1 - ): - # Add scan number to prefix for interleaved hdf5 files (only) - # NBNB Temporary fix, pending solution to hdf5 interleaving problem - scan_numbers[prefix] = scan_no = scan_numbers.get(prefix, 0) + 1 - prefix += "_s%s" % scan_no + # if ( + # path_template.suffix.endswith("h5") + # and gphl_workflow_model.characterisation_done + # and len(sweep.scans) > 1 + # ): + # # Add scan number to prefix for interleaved hdf5 files (only) + # # NBNB Temporary fix, pending solution to hdf5 interleaving problem + # scan_numbers[prefix] = scan_no = scan_numbers.get(prefix, 0) + 1 + # prefix += "_s%s" % scan_no path_template.base_prefix = prefix + logging.getLogger("HWR").info( + "Setting up sweep, image file name is %s", + path_template.get_image_file_name(), + ) + logging.getLogger("HWR").debug( + "Path template contents: %s", (path_template.as_dict()) + ) key = ( path_template.base_prefix, @@ -2117,30 +2153,50 @@ def collect_data(self, payload, correlation_id): # Handle orientations and (re) centring goniostatRotation = sweep.goniostatSweepSetting - rotation_id = orientation_id = goniostatRotation.id_ - - model_workflow_parameters = gphl_workflow_model.workflow_parameters - if not model_workflow_parameters.get("workflow_name"): - model_workflow_parameters["workflow_name"] = gphl_workflow_model.wfname - if not model_workflow_parameters.get("workflow_type"): - model_workflow_parameters["workflow_type"] = gphl_workflow_model.wftype - if not model_workflow_parameters.get("workflow_uid"): - model_workflow_parameters["workflow_uid"] = str( - HWR.beamline.gphl_connection._enactment_id - ) - if not model_workflow_parameters.get("workflow_position_id"): - # As of 20240911 all workflows use a single position, - model_workflow_parameters["workflow_position_id"] = str(uuid.uuid1()) + rotation_id = goniostatRotation.id_ + + # handle mxlims + # handle workflow parameters + new_workflow_parameters = gphl_workflow_model.workflow_parameters.copy() + wf_tracking_data = gphl_workflow_model.tracking_data + data_collection = queue_model_objects.DataCollection([acq], crystal) + # Workflow parameters for ICAT / external workflow + # The 'if' statement is to allow this to work in multiple versions + data_collection.workflow_parameters = new_workflow_parameters + tracking_data = data_collection.tracking_data + tracking_data.uuid = str(scan.id_) + tracking_data.workflow_name = wf_tracking_data.workflow_name + tracking_data.workflow_type = wf_tracking_data.workflow_type + tracking_data.workflow_uid = wf_tracking_data.uuid + tracking_data.location_id = wf_tracking_data.location_id + tracking_data.orientation_id = rotation_id if ( gphl_workflow_model.wftype == "acquisition" and not gphl_workflow_model.characterisation_done - and not model_workflow_parameters.get("workflow_characterisation_id") ): - model_workflow_parameters["workflow_characterisation_id"] = str( - sweep.id_ - ) - model_workflow_parameters["workflow_kappa_settings_id"] = str( - orientation_id + if characterisation_id is None: + # NB this is a hack - forces characterisation to be a single sweep + characterisation_id = str(sweep.id_) + tracking_data.characterisation_id = characterisation_id + wf_tracking_data.characterisation_id = characterisation_id + tracking_data.role = "Characterisation" + tracking_data.sweep_id = characterisation_id + else: + tracking_data.characterisation_id = wf_tracking_data.characterisation_id + tracking_data.role = "Result" + tracking_data.sweep_id = str(sweep.id_) + tracking_data.scan_number = gphl_workflow_model.next_scan_number + gphl_workflow_model.next_scan_number += 1 + + new_workflow_parameters["workflow_name"] = tracking_data.workflow_name + new_workflow_parameters["workflow_type"] = tracking_data.workflow_type + new_workflow_parameters["workflow_uid"] = tracking_data.workflow_uid + new_workflow_parameters["workflow_position_id"] = tracking_data.location_id + new_workflow_parameters["characterisation_id"] = ( + tracking_data.characterisation_id + ) + new_workflow_parameters["workflow_kappa_settings_id"] = ( + tracking_data.orientation_id ) initial_settings = sweep.get_initial_settings() @@ -2168,6 +2224,8 @@ def collect_data(self, payload, correlation_id): {goniostatRotation.scanAxis: scan.start} ) orientation_id = gphl_workflow_model.current_rotation_id + new_workflow_parameters["workflow_kappa_settings_id"] = orientation_id + tracking_data.orientation_id = orientation_id else: # New sweep, or recentring_mode == scan # # We need to recentre @@ -2207,11 +2265,6 @@ def collect_data(self, payload, correlation_id): acq_parameters.num_images_per_trigger * acq_parameters.osc_range - sweep_offset ) - data_collection = queue_model_objects.DataCollection([acq], crystal) - # Workflow parameters for ICAT / external workflow - # The 'if' statement is to allow this to work in multiple versions - if hasattr(data_collection, "workflow_parameters"): - data_collection.workflow_parameters.update(model_workflow_parameters) data_collections.append(data_collection) data_collection.set_enabled(True) data_collection.ispyb_group_data_collections = True @@ -2224,10 +2277,10 @@ def collect_data(self, payload, correlation_id): # debug fmt = "--> %s: %s" - print("GPHL workflow. Collect with parameters:") + logging.getLogger("HWR").debug("GPHL workflow. Collect with parameters:") for item in gphl_workflow_model.parameter_summary().items(): - print(fmt % item) - print(fmt % ("sweep_count", len(sweeps))) + logging.getLogger("HWR").debug(fmt % item) + logging.getLogger("HWR").debug(fmt % ("sweep_count", len(sweeps))) data_collection_entry = queue_manager.get_entry_with_model( self._data_collection_group @@ -2464,7 +2517,7 @@ def process_centring_request(self, payload, correlation_id): if self._data_collection_group is None: gphl_workflow_model = self._queue_entry.get_data_model() - new_dcg_name = "GΦL Translational calibration" + new_dcg_name = "GPhL Translational calibration" new_dcg_model = queue_model_objects.TaskGroup() new_dcg_model.set_enabled(True) new_dcg_model.set_name(new_dcg_name) @@ -2577,7 +2630,7 @@ def collect_centring_snapshots(self, file_name_prefix="snapshot"): summed_angle = 0.0 for snapshot_index in range(number_of_snapshots): if snapshot_index: - HWR.beamline.diffractometer.move_omega_relative(90) + HWR.beamline.diffractometer.omega.set_value_relative(90) summed_angle += 90 snapshot_filename = filename_template % ( file_name_prefix, @@ -2588,7 +2641,7 @@ def collect_centring_snapshots(self, file_name_prefix="snapshot"): self.log.debug("Centring snapshot stored at %s", snapshot_filename) collect_hwobj._take_crystal_snapshot(snapshot_filename) if summed_angle: - HWR.beamline.diffractometer.move_omega_relative(-summed_angle) + HWR.beamline.diffractometer.omega.set_value_relative(-summed_angle) def execute_sample_centring( self, centring_entry, goniostatRotation, requestedRotationId=None @@ -2854,7 +2907,7 @@ def get_emulation_samples(self): result.append(data) return result - def get_emulation_sample_dir(self, sample_name=None): + def get_emulation_sample_dir(self, sample_name=None) -> str: """If sample is a test data set for emulation, get test data directory Args: sample_name Optional[str]: @@ -2893,7 +2946,7 @@ def get_emulation_crystal_data(self, sample_name=None): raise ValueError( "Emulator crystal data file %s does not exist" % crystal_file ) - # in spite of the simcal_crystal_list name this returns an OrderdDict + # in spite of the simcal_crystal_list name this returns an OrderedDict crystal_data = f90nml.read(crystal_file)["simcal_crystal_list"] if isinstance(crystal_data, list): crystal_data = crystal_data[0] @@ -2929,7 +2982,7 @@ def receive_pre_strategy_data(self, instruction, parameters): update_dict = self.update_reference_files(parameters) except: self.log.error( - "Error in GΦL parameter update for %s, Continuing ...", + "Error in GPhL parameter update for %s, Continuing ...", instruction, ) finally: @@ -2976,7 +3029,7 @@ def receive_pre_collection_data(self, instruction, parameters): update_dict = self.adjust_dose(parameters) except: self.log.error( - "Error in GΦL parameter update for %s, Continuing ...", + "Error in GPhL parameter update for %s, Continuing ...", instruction, ) finally: @@ -3191,6 +3244,32 @@ def adjust_transmission(self, values): result["dose_budget"] = {"highlight": "OK"} return result + def derive_maximum_chi(self) -> float | None: + """Give maximum chi value (in degrees) derived from kappa motor limits + and rotation axis directions""" + margin = 0.1 # safety margin in degrees, to avoid overrunning kappa limit + omega_axis = self.rotation_axes.get("phi") + kappa_axis = self.rotation_axes.get("kappa") + # First make sure we have a kappa axis, just in case + if omega_axis and kappa_axis: + omega_axis = numpy.array(omega_axis) + omega_axis /= numpy.linalg.norm(omega_axis) + kappa_axis = numpy.array(kappa_axis) + kappa_axis /= numpy.linalg.norm(kappa_axis) + cos_alpha = omega_axis.dot(kappa_axis) + result = 2 * math.acos(cos_alpha) + kappa_limits = HWR.beamline.diffractometer.kappa.get_limits() + if None not in kappa_limits: + kappa_max = max(kappa_limits) + if kappa_max and kappa_max < 180: + kappa_max = math.radians(kappa_max) + result = math.acos( + 1 + (1 - cos_alpha**2) * (math.cos(kappa_max) - 1) + ) + return math.degrees(result) - margin + else: + return None + def validate_url(value: str) -> bool: """Validate url string""" diff --git a/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py b/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py index ddc61ab925..02d5709b4f 100644 --- a/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py +++ b/mxcubecore/HardwareObjects/Gphl/GphlWorkflowConnection.py @@ -173,7 +173,7 @@ def get_bdg_licence_dir(self, name): def open_connection(self): if self._gateway is None: - self.log.debug("Opening GΦL connection") + self.log.debug("Opening GPhL connection") else: return @@ -196,7 +196,7 @@ def open_connection(self): java_parameters["port"] = val self.log.debug( - "Opening GΦL connection: %s ", + "Opening GPhL connection: %s ", (", ".join("%s:%s" % tt0 for tt0 in sorted(params.items()))), ) @@ -324,14 +324,14 @@ def start_workflow(self, workflow_queue, workflow_model_obj): os.makedirs(wdir) except: # No need to raise error - program will fail downstream - self.log.error("Could not create GΦL working directory: %s", wdir) + self.log.error("Could not create GPhL working directory: %s", wdir) for ss0 in command_list: ss0 = ss0.rsplit("=", maxsplit=1)[-1] if ss0.startswith("/") and "*" not in ss0 and not os.path.exists(ss0): self.log.warning("File does not exist : %s", ss0) - self.log.info("GΦL execute :\n%s", " ".join(command_list)) + self.log.info("GPhL execute :\n%s", " ".join(command_list)) # Get environmental variables envs = os.environ.copy() @@ -358,7 +358,7 @@ def start_workflow(self, workflow_queue, workflow_model_obj): if runworkflow_opts: envs["RUNWORKFLOW_OPTS"] = " ".join(runworkflow_opts) - self.log.debug("Executing GΦL workflow, in environment %s", envs) + self.log.debug("Executing GPhL workflow, in environment %s", envs) try: self._running_process = subprocess.Popen(command_list, env=envs) except Exception: @@ -370,7 +370,7 @@ def start_workflow(self, workflow_queue, workflow_model_obj): self.update_state(self.STATES.READY) self.log.debug( - "GΦL workflow pid, returncode : %s, %s" + "GPhL workflow pid, returncode : %s, %s" % (self._running_process.pid, self._running_process.returncode) ) @@ -379,7 +379,7 @@ def workflow_ended(self): # No workflow to abort return - self.log.debug("GΦL workflow ended") + self.log.debug("GPhL workflow ended") self.update_state(self.STATES.OFF) if self._await_result is not None: # We are awaiting an answer - give an abort @@ -412,7 +412,7 @@ def workflow_ended(self): self.log.info("Error was:", exc_info=True) def close_connection(self): - self.log.debug("GΦL Close connection ") + self.log.debug("GPhL Close connection ") xx0 = self._gateway self._gateway = None if xx0 is not None: @@ -455,7 +455,7 @@ def processText(self, py4j_message): correlation_id = xx0.correlation_id if not payload: - self.log.warning("GΦL Empty or unparsable information message. Ignored") + self.log.warning("GPhL Empty or unparsable information message. Ignored") elif self.workflow_queue is not None: # Could happen if we have ended the workflow self.workflow_queue.put_nowait( @@ -507,13 +507,16 @@ def processMessage(self, py4j_message): correlation_id = xx0.correlation_id if self._enactment_id is None: - # NB this should be made less primitive - # once we are past direct function calls - self._enactment_id = xx0.enactment_id + enactment_id = xx0.enactment_id + if enactment_id: + self._enactment_id = enactment_id + self.workflow_queue.put_nowait( + ("StartEnactment", enactment_id, None, None) + ) elif not payload: self.log.error( - "GΦL message lacks payload - sending 'Abort' to external workflow" + "GPhL message lacks payload - sending 'Abort' to external workflow" ) return self._response_to_server( GphlMessages.BeamlineAbort(), correlation_id @@ -569,7 +572,7 @@ def processMessage(self, py4j_message): self.workflow_ended() else: self.log.debug( - "GΦL - response=%s messageId=%s" + "GPhL - response=%s messageId=%s" % (result.__class__.__name__, correlation_id) ) return self._response_to_server(result, correlation_id) @@ -585,7 +588,7 @@ def processMessage(self, py4j_message): return None else: - self.log.error("GΦL Unknown message type: %s - aborting", message_type) + self.log.error("GPhL Unknown message type: %s - aborting", message_type) return self._response_to_server( GphlMessages.BeamlineAbort(), correlation_id ) @@ -622,7 +625,7 @@ def _decode_py4j_message(self, py4j_message): converter = getattr(self, converterName) except AttributeError: self.log.error( - "GΦL Message type %s not recognised (no %s function)" + "GPhL Message type %s not recognised (no %s function)" % (message_type, converterName) ) payload = None @@ -632,7 +635,7 @@ def _decode_py4j_message(self, py4j_message): payload = converter(py4j_message.getPayload()) except NotImplementedError: self.log.error( - "Processing of GΦL message %s not implemented", message_type + "Processing of GPhL message %s not implemented", message_type ) payload = None return GphlMessages.ParsedMessage( diff --git a/mxcubecore/HardwareObjects/Gphl/Transcal2MiniKappa.py b/mxcubecore/HardwareObjects/Gphl/Transcal2MiniKappa.py index 71c4005dd9..48555da422 100644 --- a/mxcubecore/HardwareObjects/Gphl/Transcal2MiniKappa.py +++ b/mxcubecore/HardwareObjects/Gphl/Transcal2MiniKappa.py @@ -272,7 +272,7 @@ def get_minikappa_data(configfile): parser.add_argument( "--minikappa_config", metavar="minikappa_config", - help="minikappa-correction.xmll file\n", + help="minikappa-correction.xml file\n", ) argsobj = parser.parse_args() diff --git a/mxcubecore/HardwareObjects/MD3UP.py b/mxcubecore/HardwareObjects/MD3UP.py index 74b1fba655..4956182315 100644 --- a/mxcubecore/HardwareObjects/MD3UP.py +++ b/mxcubecore/HardwareObjects/MD3UP.py @@ -88,7 +88,7 @@ def setNbImages(self, number_of_images): self.scan_nb_frames = number_of_images def oscilScan(self, start, end, exptime, number_of_images, wait=False): - if self.in_plate_mode(): + if self.in_plate_mode: scan_speed = math.fabs(end - start) / exptime low_lim, hi_lim = map(float, self.scanLimits(scan_speed)) if start < low_lim: @@ -124,7 +124,7 @@ def oscilScan(self, start, end, exptime, number_of_images, wait=False): def oscilScan4d( self, start, end, exptime, number_of_images, motors_pos, wait=False ): - if self.in_plate_mode(): + if self.in_plate_mode: scan_speed = math.fabs(end - start) / exptime low_lim, hi_lim = map(float, self.scanLimits(scan_speed)) if start < low_lim: diff --git a/mxcubecore/HardwareObjects/MicroDiffractometer.py b/mxcubecore/HardwareObjects/MicroDiffractometer.py index 2441b50b6a..6fd388ed8a 100644 --- a/mxcubecore/HardwareObjects/MicroDiffractometer.py +++ b/mxcubecore/HardwareObjects/MicroDiffractometer.py @@ -49,7 +49,7 @@ def init(self): exporter_address = self.get_property("exporter_address") _host, _port = exporter_address.split(":") self._exporter = Exporter(_host, int(_port)) - self.head_type = self._get_head_type + self.head_type = self._head_type # add the custom commands for nam, cmd in self.get_property("commands").items(): @@ -211,7 +211,7 @@ def find_elem(ddict, val): return motors_dict @property - def _get_head_type(self) -> DiffractometerHead: + def _head_type(self) -> DiffractometerHead: """Get the head type.""" try: self.head_type = DiffractometerHead( diff --git a/mxcubecore/HardwareObjects/Microdiff.py b/mxcubecore/HardwareObjects/Microdiff.py index 36e235df84..b35622d4b9 100644 --- a/mxcubecore/HardwareObjects/Microdiff.py +++ b/mxcubecore/HardwareObjects/Microdiff.py @@ -350,7 +350,7 @@ def getMotorToExporterNames(self): "sampy": self.sampleYMotor.get_property("actuator_name"), "zoom": "Zoom", } - if self.in_kappa_mode(): + if self.in_kappa_mode: MOTOR_TO_EXPORTER_NAME.update( {"kappa": self.kappaMotor.get_property("actuator_name")} ) @@ -484,7 +484,7 @@ def set_phase(self, phase, wait=False, timeout=None): self.close_detector_cover() self.phase_prepare(phase) - if _use_custom and not self.in_plate_mode(): + if _use_custom and not self.in_plate_mode: script = "ChangePhase_" + phase.lower() msg = f"Changing phase to {phase}, using pmac script" logging.getLogger("user_level_log").info(msg) @@ -503,7 +503,7 @@ def get_phase_list(self): return list(self.phases.keys()) def move_sync_motors(self, motors_dict, wait=False, timeout=None): - in_kappa_mode = self.in_kappa_mode() + in_kappa_mode = self.in_kappa_mode argin = "" # print "start moving motors =============", time.time() if wait: @@ -529,7 +529,7 @@ def move_sync_motors(self, motors_dict, wait=False, timeout=None): # print "end moving motors =============", time.time() def oscilScan(self, start, end, exptime, number_of_images, wait=False): - if self.in_plate_mode(): + if self.in_plate_mode: scan_speed = math.fabs(end - start) / exptime low_lim, hi_lim = map(float, self.scanLimits(scan_speed)) if start < low_lim: @@ -564,7 +564,7 @@ def oscilScan(self, start, end, exptime, number_of_images, wait=False): def oscilScan4d( self, start, end, exptime, number_of_images, motors_pos, wait=False ): - if self.in_plate_mode(): + if self.in_plate_mode: scan_speed = math.fabs(end - start) / exptime low_lim, hi_lim = map(float, self.scanLimits(scan_speed)) if start < low_lim: @@ -692,7 +692,7 @@ def characterisation_scan( wait (bool); Wait (True) or no (False) the end of the command. """ - if self.in_plate_mode(): + if self.in_plate_mode: # to see if needed when plates return scan_params = "%d\t%0.3f\t%0.3f\t" % (nb_frames, start, scan_range) @@ -714,12 +714,14 @@ def characterisation_scan( self._wait_ready(20 * 60) # timeout of 15 min print("finished at ---------->", time.time()) + @property def in_plate_mode(self): try: return self.head_type.get_value() == "Plate" except Exception: return False + @property def in_kappa_mode(self): return ( self.head_type.get_value() == "MiniKappa" and self.kappa_channel.get_value() @@ -735,8 +737,8 @@ def get_motors(self): "sampx": self.sampleXMotor, "sampy": self.sampleYMotor, "zoom": self.zoomMotor, - "kappa": self.kappaMotor if self.in_kappa_mode() else None, - "kappa_phi": self.kappaPhiMotor if self.in_kappa_mode() else None, + "kappa": self.kappaMotor if self.in_kappa_mode else None, + "kappa_phi": self.kappaPhiMotor if self.in_kappa_mode else None, } def get_positions(self): @@ -749,10 +751,10 @@ def get_positions(self): "sampy": float(self.sampleYMotor.get_value()), "zoom": self.zoomMotor.get_value().value, "kappa": ( - float(self.kappaMotor.get_value()) if self.in_kappa_mode() else None + float(self.kappaMotor.get_value()) if self.in_kappa_mode else None ), "kappa_phi": ( - float(self.kappaPhiMotor.get_value()) if self.in_kappa_mode() else None + float(self.kappaPhiMotor.get_value()) if self.in_kappa_mode else None ), } return pos @@ -761,7 +763,7 @@ def move_motors(self, roles_positions_dict): self.move_sync_motors(roles_positions_dict, wait=True) def move_to_beam(self, x, y): - if not self.in_plate_mode(): + if not self.in_plate_mode: MiniDiff.MiniDiff.move_to_beam(self, x, y) else: try: @@ -790,7 +792,7 @@ def start_manual_centring(self, sample_info=None): self.log.info("Starting centring procedure ...") - if self.in_plate_mode(): + if self.in_plate_mode: plateTranslation = self.get_object_by_role("plateTranslation") cmd_set_plate_vertical = self.add_command( { diff --git a/mxcubecore/HardwareObjects/MicrodiffFocusMotor.py b/mxcubecore/HardwareObjects/MicrodiffFocusMotor.py index 9eefe00ee3..6d74a3e2a4 100644 --- a/mxcubecore/HardwareObjects/MicrodiffFocusMotor.py +++ b/mxcubecore/HardwareObjects/MicrodiffFocusMotor.py @@ -7,7 +7,7 @@ def __init__(self, name): ExpMotor.__init__(self, name) def init(self): - if HWR.beamline.diffractometer.in_plate_mode(): + if HWR.beamline.diffractometer.in_plate_mode: self.actuator_name = self.get_property("centring_focus") else: self.actuator_name = self.get_property("alignment_focus") diff --git a/mxcubecore/HardwareObjects/MiniDiff.py b/mxcubecore/HardwareObjects/MiniDiff.py index ccc8036112..48d2069c50 100644 --- a/mxcubecore/HardwareObjects/MiniDiff.py +++ b/mxcubecore/HardwareObjects/MiniDiff.py @@ -59,7 +59,6 @@ def __init__(self, *args): self.pixelsPerMmY = None self.pixelsPerMmZ = None self.centredTime = 0 - self.user_confirms_centring = True self.do_centring = True self.chiAngle = 0.0 @@ -396,6 +395,7 @@ def is_valid(self): and HWR.beamline.sample_view.camera is not None ) + @property def in_plate_mode(self): return False @@ -1010,7 +1010,7 @@ def take_snapshot(self, image_path_list: list) -> None: ) HWR.beamline.sample_view.save_snapshot(path=image_path) # do not move 90 degrees if not needed - if not self.in_plate_mode() and snapshot_index < len(image_path_list) - 1: + if not self.in_plate_mode and snapshot_index < len(image_path_list) - 1: self.phiMotor.set_value_relative(90, timeout=5) def snapshotsDone(self, snapshotsProcedure): diff --git a/mxcubecore/HardwareObjects/QtGraphicsManager.py b/mxcubecore/HardwareObjects/QtGraphicsManager.py index ac1484615e..1c649bc80e 100644 --- a/mxcubecore/HardwareObjects/QtGraphicsManager.py +++ b/mxcubecore/HardwareObjects/QtGraphicsManager.py @@ -71,6 +71,7 @@ def __init__(self, name): AbstractSampleView.__init__(self, name) self.diffractometer_hwobj = None + self.camera_hwobj = None self.graphics_config_filename = None self.omega_angle = 0 @@ -206,9 +207,11 @@ def init(self): if self.diffractometer_hwobj is not None: pixels_per_mm = self.diffractometer_hwobj.get_pixels_per_mm() self.diffractometer_pixels_per_mm_changed(pixels_per_mm) - GraphicsLib.GraphicsItemGrid.set_grid_direction( - self.diffractometer_hwobj.get_grid_direction() - ) + # TODO NBNB BROKEN since 20260217 - rhfogh + # Must be replaced. Meanwhile commented out temporarily + # GraphicsLib.GraphicsItemGrid.set_grid_direction( + # self.diffractometer_hwobj.get_grid_direction() + # ) self.connect( self.diffractometer_hwobj, @@ -259,6 +262,8 @@ def init(self): self.log.error("GraphicsManager: Diffractometer hwobj not defined") if HWR.beamline.beam is not None: + # Call get_value. Necessary, as this triggers update + yes_we_need_this_call = HWR.beamline.beam.get_value() self.beam_info_dict = HWR.beamline.beam.get_beam_info_dict() self.beam_position = HWR.beamline.beam.get_beam_position_on_screen() self.connect( @@ -1095,9 +1100,9 @@ def mouse_wheel_scrolled(self, delta): Rotates omega axis up or down """ if delta > 0: - self.diffractometer_hwobj.move_omega_relative(self.omega_move_delta) + self.diffractometer_hwobj.omega.set_value_relative(self.omega_move_delta) else: - self.diffractometer_hwobj.move_omega_relative(-self.omega_move_delta) + self.diffractometer_hwobj.omega.set_value_relative(-self.omega_move_delta) def item_clicked(self, item, state): """Item clicked event @@ -1454,7 +1459,7 @@ def save_scene_animation(self, filename, duration_sec=1): """Save animation task""" # self.diffractometer_hwobj.set_ready(False) - gevent.spawn(self.diffractometer_hwobj.move_omega_relative, 180) + gevent.spawn(self.diffractometer_hwobj.omega.set_value_relative, 180) gevent.spawn(self.save_scene_animation_task, filename, duration_sec) def save_scene_animation_task(self, filename, duration_sec): @@ -1936,7 +1941,7 @@ def auto_grid_procedure(self): "optical_y": y, } ) - self.diffractometer_hwobj.move_omega_relative(360 / number_of_snapshots) + self.diffractometer_hwobj.omega.set_value_relative(360 / number_of_snapshots) auto_mesh = AutoMesh.getAutoMesh( background_image, diff --git a/mxcubecore/HardwareObjects/SOLEIL/PX1/PX1MiniDiff.py b/mxcubecore/HardwareObjects/SOLEIL/PX1/PX1MiniDiff.py index 1617c29e54..d0e4b53633 100644 --- a/mxcubecore/HardwareObjects/SOLEIL/PX1/PX1MiniDiff.py +++ b/mxcubecore/HardwareObjects/SOLEIL/PX1/PX1MiniDiff.py @@ -161,7 +161,7 @@ def centring_done(self, centring_procedure): # done already by px1_center pass # if 3 click centring move -180 - # if not self.in_plate_mode(): + # if not self.in_plate_mode: # self.wait_device_ready() # self.motor_hwobj_dict['phi'].set_value_relative(-180, timeout=None) diff --git a/mxcubecore/HardwareObjects/SOLEIL/PX2/PX2Diffractometer.py b/mxcubecore/HardwareObjects/SOLEIL/PX2/PX2Diffractometer.py index 975953d5e5..c60d81b8bb 100644 --- a/mxcubecore/HardwareObjects/SOLEIL/PX2/PX2Diffractometer.py +++ b/mxcubecore/HardwareObjects/SOLEIL/PX2/PX2Diffractometer.py @@ -236,7 +236,7 @@ def use_sample_changer(self): """ Description: """ - return not self.in_plate_mode() + return not self.in_plate_mode def beam_position_changed(self, value): self.beam_position = value @@ -1069,7 +1069,7 @@ def visual_align(self, point_1, point_2): """ Descript. : """ - if self.in_plate_mode(): + if self.in_plate_mode: self.log.info("PX2Diffractometer: Visual align not available in Plate mode") else: t1 = [point_1.sampx, point_1.sampy, point_1.phiy] diff --git a/mxcubecore/HardwareObjects/abstract/AbstractCollect.py b/mxcubecore/HardwareObjects/abstract/AbstractCollect.py index 26c84daaa6..4d198a1469 100644 --- a/mxcubecore/HardwareObjects/abstract/AbstractCollect.py +++ b/mxcubecore/HardwareObjects/abstract/AbstractCollect.py @@ -744,7 +744,7 @@ def get_sample_info(self) -> None: self.current_dc_parameters["blSampleId"] = sample_id - if HWR.beamline.diffractometer.in_plate_mode(): + if HWR.beamline.diffractometer.in_plate_mode: # TODO store plate location in lims pass elif HWR.beamline.sample_changer: @@ -821,10 +821,10 @@ def take_crystal_snapshots(self) -> None: ] = snapshot_filename self._take_crystal_snapshot(snapshot_filename) if number_of_snapshots > 1: - HWR.beamline.diffractometer.move_omega_relative(90) + HWR.beamline.diffractometer.omega.set_value_relative(90) if ( - not HWR.beamline.diffractometer.in_plate_mode() + not HWR.beamline.diffractometer.in_plate_mode and self.current_dc_parameters.get("take_video") ): # Add checkbox to allow enable/disable creation of gif diff --git a/mxcubecore/HardwareObjects/abstract/AbstractDiffractometer.py b/mxcubecore/HardwareObjects/abstract/AbstractDiffractometer.py index e5b6d1cb0c..11585a557a 100644 --- a/mxcubecore/HardwareObjects/abstract/AbstractDiffractometer.py +++ b/mxcubecore/HardwareObjects/abstract/AbstractDiffractometer.py @@ -333,7 +333,7 @@ def get_value_motors(self, motors_list: list | None = None) -> dict: mot_pos_dict[str(motor)] = float(mot_hwobj_dict[motor].get_value()) except KeyError: msg = f"Invalid motor name {motor}" - self.log.exceptionb(msg) + self.log.exception(msg) except TypeError: msg = f"No value for {motor}" self.log.warning(msg) @@ -378,31 +378,23 @@ def get_state_motors(self, motors_list: list | None = None) -> dict: # -------- Head Type and Modes -------- - @property - def get_head_type(self) -> DiffractometerHead: - """Get the head type - Returns: - DiffractometerHead member. - """ - return self.head_type - @property def in_plate_mode(self) -> bool: """Check if the head is a plate.""" - return self.get_head_type == DiffractometerHead.PLATE + return self.head_type == DiffractometerHead.PLATE @property def in_kappa_mode(self) -> bool: """Check if the head is MiniKappa.""" - return self.get_head_type == DiffractometerHead.MINI_KAPPA + return self.head_type == DiffractometerHead.MINI_KAPPA @property def in_chip_mode(self) -> bool: """Check if there is chip configuration of the head.""" return ( - self.get_head_type == DiffractometerHead.SSX + self.head_type == DiffractometerHead.SSX and self.current_constraint == DiffractometerConstraint.STILL ) @@ -410,12 +402,12 @@ def in_chip_mode(self) -> bool: def in_injector_mode(self) -> bool: """Check if there is injector on the head.""" return ( - self.get_head_type == DiffractometerHead.SSX + self.head_type == DiffractometerHead.SSX and self.current_constraint == DiffractometerConstraint.INJECTOR ) @property - def get_head_enum(self): + def head_enum(self): """Get the diffractometer head Enum. Used when no import wished.""" return DiffractometerHead diff --git a/mxcubecore/configuration/alba_xaloc13/gphl_beamline_config/instrumentation.nml b/mxcubecore/configuration/alba_xaloc13/gphl_beamline_config/instrumentation.nml index f70e3481a0..4d70275141 100644 --- a/mxcubecore/configuration/alba_xaloc13/gphl_beamline_config/instrumentation.nml +++ b/mxcubecore/configuration/alba_xaloc13/gphl_beamline_config/instrumentation.nml @@ -62,7 +62,7 @@ gonio_centring_axis_names= 'phiy', 'sampx', 'sampy' ! At omega=0: centx antiparallel to beam, centy perpendicular. ! omegax parallel to omega -!? FIXME! Sort out ID30B beamstop on the fly (or delete it?) +!? FIXME! Sort out ID30B beamstop on the fly (or delete it?) ! Need beamstop for I04 for now, to get a realistic beamstop shadow ! on simulated images. It is not needed for stratcal because I04 has ! no collisions and the shadow will not have any effect on the statistics @@ -82,545 +82,3 @@ xds_DETECTOR= PILATUS xds_OVERLOAD= 1048500 / - -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 1 195 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 1 195 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 1 195 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 1 195 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 1 195 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 213 407 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 213 407 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 213 407 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 213 407 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 213 407 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 425 619 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 425 619 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 425 619 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 425 619 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 425 619 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 637 831 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 637 831 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 637 831 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 637 831 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 637 831 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 849 1043 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 849 1043 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 849 1043 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 849 1043 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 849 1043 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 1061 1255 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 1061 1255 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 1061 1255 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 1061 1255 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 1061 1255 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 1273 1467 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 1273 1467 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 1273 1467 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 1273 1467 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 1273 1467 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 1485 1679 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 1485 1679 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 1485 1679 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 1485 1679 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 1485 1679 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 1697 1891 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 1697 1891 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 1697 1891 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 1697 1891 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 1697 1891 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 1909 2103 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 1909 2103 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 1909 2103 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 1909 2103 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 1909 2103 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 2121 2315 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 2121 2315 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 2121 2315 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 2121 2315 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 2121 2315 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1 487 - SEG_NY_LIMITS= 2333 2527 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 495 981 - SEG_NY_LIMITS= 2333 2527 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 989 1475 - SEG_NY_LIMITS= 2333 2527 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1483 1969 - SEG_NY_LIMITS= 2333 2527 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ -&SEGMENT_LIST - SEG_NX_LIMITS= 1977 2463 - SEG_NY_LIMITS= 2333 2527 - SEG_ORG_X= 0.0000000 - SEG_ORG_Y= 0.0000000 - SEG_COORD= 0.0000000 - SEG_X_AXIS= 1.0000000 0.0000000 0.0000000 - SEG_Y_AXIS= 0.0000000 1.0000000 0.0000000 -/ - diff --git a/mxcubecore/configuration/embl_hh_p14/gphl_beamline_config/instrumentation.nml b/mxcubecore/configuration/embl_hh_p14/gphl_beamline_config/instrumentation.nml index c822f5b75b..a5100e724d 100644 --- a/mxcubecore/configuration/embl_hh_p14/gphl_beamline_config/instrumentation.nml +++ b/mxcubecore/configuration/embl_hh_p14/gphl_beamline_config/instrumentation.nml @@ -451,293 +451,4 @@ gonio_centring_axis_names= 'phiy', 'sampx', 'sampy' 178, 179, 180, 179, 180, 181, 180, 181, 182 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1 1030 -SEG_NY_LIMITS= 1 514 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1041 2070 -SEG_NY_LIMITS= 1 514 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 2081 3110 -SEG_NY_LIMITS= 1 514 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 3121 4148 -SEG_NY_LIMITS= 1 514 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1 1030 -SEG_NY_LIMITS= 552 1065 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1041 2070 -SEG_NY_LIMITS= 552 1065 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 2081 3110 -SEG_NY_LIMITS= 552 1065 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 3121 4148 -SEG_NY_LIMITS= 552 1065 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1 1030 -SEG_NY_LIMITS= 1103 1616 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1041 2070 -SEG_NY_LIMITS= 1103 1616 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 2081 3110 -SEG_NY_LIMITS= 1103 1616 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 3121 4148 -SEG_NY_LIMITS= 1103 1616 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1 1030 -SEG_NY_LIMITS= 1654 2167 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1041 2070 -SEG_NY_LIMITS= 1654 2167 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 2081 3110 -SEG_NY_LIMITS= 1654 2167 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 3121 4148 -SEG_NY_LIMITS= 1654 2167 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1 1030 -SEG_NY_LIMITS= 2205 2718 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1041 2070 -SEG_NY_LIMITS= 2205 2718 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 2081 3110 -SEG_NY_LIMITS= 2205 2718 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 3121 4148 -SEG_NY_LIMITS= 2205 2718 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1 1030 -SEG_NY_LIMITS= 2756 3269 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1041 2070 -SEG_NY_LIMITS= 2756 3269 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 2081 3110 -SEG_NY_LIMITS= 2756 3269 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 3121 4148 -SEG_NY_LIMITS= 2756 3269 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1 1030 -SEG_NY_LIMITS= 3307 3820 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1041 2070 -SEG_NY_LIMITS= 3307 3820 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 2081 3110 -SEG_NY_LIMITS= 3307 3820 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 3121 4148 -SEG_NY_LIMITS= 3307 3820 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1 1030 -SEG_NY_LIMITS= 3858 4362 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 1041 2070 -SEG_NY_LIMITS= 3858 4362 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 2081 3110 -SEG_NY_LIMITS= 3858 4362 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ -&SEGMENT_LIST -SEG_X_AXIS=1.0000000, 0.0000000, 0.0000000 -SEG_Y_AXIS=0.0000000, 1.0000000, 0.0000000 -SEG_NX_LIMITS= 3121 4148 -SEG_NY_LIMITS= 3858 4362 -SEG_ORG_X=0.0000000 -SEG_ORG_Y=0.0000000 -SEG_COORD=0.0000000 -/ - +/ \ No newline at end of file diff --git a/mxcubecore/configuration/mockup/gphl/gphl-workflow.yml b/mxcubecore/configuration/mockup/gphl/gphl-workflow.yml index 148c0a0ba8..e768d0f0af 100644 --- a/mxcubecore/configuration/mockup/gphl/gphl-workflow.yml +++ b/mxcubecore/configuration/mockup/gphl/gphl-workflow.yml @@ -209,7 +209,7 @@ configuration: # Set in gph-setup.xml # gphl_subdir is not a workflow option. It is set in gphl-setup.xml workflows: - "GΦL Acquisition Workflow": + "GPhL Acquisition Workflow": wfpath: Gphl wftype: acquisition requires: @@ -239,10 +239,10 @@ configuration: wf_selection: mxexpt variants: - advanced - - minimal + # - minimal - basic - - ultralong - - twotransmission + # - ultralong + # - twotransmission options: # Filename pattern name for normal data collections pattern: interleaved @@ -257,7 +257,7 @@ configuration: variants: - advanced - basic - - ultralong + # - ultralong documentation: | Single wavelength SAD data acquisition, advanced strategy. Aligned on even symetry axes or inverse beam. @@ -334,7 +334,7 @@ configuration: # File name pattern for characterisation data collection charpattern: generic - "GΦL Diffractometer calibration": + "GPhL Diffractometer calibration": wfpath: Gphl wftype: diffractcal requires: @@ -382,7 +382,7 @@ configuration: # # Path of file to dump persisted instrumentation configuration. OPTIONAL # instcfgout: gphl_diffractcal_out.nml - "GΦL Translational Calibration": + "GPhL Translational Calibration": wfpath: Gphl wftype: transcal requires: diff --git a/mxcubecore/configuration/mockup/gphl/gphl_wf_automation_interface.yml b/mxcubecore/configuration/mockup/gphl/gphl_wf_automation_interface.yml index 1d8c28367c..2d36b3d5af 100644 --- a/mxcubecore/configuration/mockup/gphl/gphl_wf_automation_interface.yml +++ b/mxcubecore/configuration/mockup/gphl/gphl_wf_automation_interface.yml @@ -6,15 +6,15 @@ # It shows the interfaces between the Global Phasing workflow and a calling # automation program running through MXCuBE,. -# Adding a GΦL workflow is done in the same way as adding an EDNA workflow. +# Adding a GPhL workflow is done in the same way as adding an EDNA workflow. # The parameters are a node ID and a task dictionary. -# The parameters relevant for GΦL are, in yaml notation: +# The parameters relevant for GPhL are, in yaml notation: task: parameters: # Process control and ??? - wfpath: Gphl # Mandatory – specifies GΦL workflow + wfpath: Gphl # Mandatory – specifies GPhL workflow automation_mode: MASSIF1 # Optional, defaults to None (manual) shape: some_shape_name # Used for EDNA workflow, I do not know what for @@ -44,7 +44,7 @@ task: # The dictionaries are applied to the acquisitions in order; # if there is only one dictionary in the list, it applies to all acquisitions. # The following appear in task dictionaries in interactive MXCuBE3, - # but are NOT used by the GΦL workflow: wfname, wftype, strategies, type + # but are NOT used by the GPhL workflow: wfname, wftype, strategies, type # The first dictionary are the parameters needed for MASSIF1 characterisation # where characterisation and first XDS run are done BEFORE starting the workflow diff --git a/mxcubecore/configuration/mockup/gphl/gphl_wf_test_parameters.yml b/mxcubecore/configuration/mockup/gphl/gphl_wf_test_parameters.yml index b3ab307dc0..5910b4b57f 100644 --- a/mxcubecore/configuration/mockup/gphl/gphl_wf_test_parameters.yml +++ b/mxcubecore/configuration/mockup/gphl/gphl_wf_test_parameters.yml @@ -6,15 +6,15 @@ # It shows the interfaces between the Global Phasing workflow and a calling # automation program running through MXCuBE,. -# Adding a GΦL workflow is done in the same way as adding an EDNA workflow. +# Adding a GPhL workflow is done in the same way as adding an EDNA workflow. # The parameters are a node ID and a task dictionary. -# The parameters relevant for GΦL are, in yaml notation: +# The parameters relevant for GPhL are, in yaml notation: task: parameters: # Web version only - otherwise ignored - wfpath: Gphl # Mandatory for web version– specifies GΦL workflow + wfpath: Gphl # Mandatory for web version– specifies GPhL workflow shape: some_shape_name # Used for EDNA workflow, I do not know what for # automation_mode: MASSIF1 # Optional, defaults to value in settings @@ -44,7 +44,7 @@ task: # The dictionaries are applied to the acquisitions in order; # if there is only one dictionary in the list, it applies to all acquisitions. # The following appear in task dictionaries in interactive MXCuBE3, but are NOT - # used by the GΦL workflow: wfname, wftype, strategies, type + # used by the GPhL workflow: wfname, wftype, strategies, type # The first dictionary are the parameters needed for MASSIF1 characterisation # init_spot_dir sets a mode where characterisation and first XDS run are diff --git a/mxcubecore/model/queue_model_objects.py b/mxcubecore/model/queue_model_objects.py index f943ae170d..4b6b67f038 100644 --- a/mxcubecore/model/queue_model_objects.py +++ b/mxcubecore/model/queue_model_objects.py @@ -28,6 +28,9 @@ import copy import logging import os +from typing import Any, Optional + +from pydantic import BaseModel, Field from mxcubecore.model import queue_model_enumerables @@ -53,6 +56,66 @@ __license__ = "LGPLv3+" +class TrackingData(BaseModel): + """Data to connect different tasks into workflows, LIMS input, MXLIMS, etc. + + NB Should be harmonised and merged with workflow_parameters""" + + uuid: Optional[str] = Field( + default=None, + description="Unique identifier string for this queue_model_object", + ) + workflow_name: Optional[str] = Field( + default=None, + description="Name of workflow that this queue_model_object belongs to", + ) + workflow_type: Optional[str] = Field( + default=None, + description="Type of workflow that this queue_model_object belongs to", + ) + workflow_uid: Optional[str] = Field( + default=None, + description="Unique identifier string for the workflow this queue_model_object belongs to", + ) + location_id: Optional[str] = Field( + default=None, + description="Unique identifier string for the location / LogisticalSample " + "of this queue_model_object", + ) + orientation_id: Optional[str] = Field( + default=None, + description="Unique identifier string for the orientation (kappa/phi/chi settings) " + "for this queue_model_object", + ) + characterisation_id: Optional[str] = Field( + default=None, + description="Unique identifier string for characterisation data acquisition " + "that is relevant for this queue_model_object", + ) + sweep_id: Optional[str] = Field( + default=None, + description="Unique identifier string for the sweep that this queue_model_object " + "is part of. Used to combine multiple Acquisitions as scans of a single sweep.", + ) + scan_number: Optional[int] = Field( + default=None, + description="Ordinal number (starting at 0), for this queue_model_object " + "in the experiment. Defines the time ordering of acquisitions and scans.", + ) + role: Optional[str] = Field( + default=None, + description="Role of this Task result within the experiment.", + json_schema_extra={ + "examples": [ + "Result", + "Intermediate", + "Characterisation", + "Centring", + ], + }, + ) + + class TaskNode(object): """ Objects that inherit TaskNode can be added to and handled by @@ -72,6 +135,8 @@ def __init__(self, task_data=None): self._requires_centring = True self._origin = None self._task_data = task_data + # tracking data for connecting jobs into workflows, mxlims output, etrc. + self.tracking_data: TrackingData = TrackingData() @property def task_data(self): @@ -1957,6 +2022,10 @@ def get_path_template(self): class GphlWorkflow(TaskNode): + + # Key for gphl workflow extensions used in MXLIMS + GPHL_WORKFLOW_EXTENSION = "workflow.gphl.co" + def __init__(self): TaskNode.__init__(self) @@ -1971,6 +2040,7 @@ def __init__(self): self.maximum_dose_budget = 20.0 self.decay_limit = 25 self.characterisation_budget_fraction = 0.05 + self.enactment_id = None # string. Only active mode currently is 'MASSIF1' self.automation_mode = None @@ -1989,7 +2059,6 @@ def __init__(self): self.aimed_resolution = None # from 'resolution' parameter or defaults self.wavelengths = () # from 'energies' parameters self.use_cell_for_processing = False - self.strategy_variant = None # from 'strategy' Used for acquisition self.strategy_options = {} self.relative_rad_sensitivity = 1.0 # Directory containing SPOT.XDS file @@ -2027,6 +2096,9 @@ def __init__(self): # Factor to account for transmission not being uniform self.dose_correction_factor = 1.0 + # Scan number for MXLIMS Scan ordering + self.next_scan_number = 0 + # Workflow attributes - for passing to LIMS (conf Olof Svensson) self.workflow_parameters = {} @@ -2071,15 +2143,11 @@ def parameter_summary(self): summary["wavelengths"] = tuple(x.wavelength for x in self.wavelengths) summary["resolution"] = self.detector_setting.resolution summary["orgxy"] = self.detector_setting.orgxy - summary["strategy_variant"] = self.strategy_options.get("variant", "not set") + summary["strategy_variant"] = self.strategy_variant summary["orientation_count"] = len(self.goniostat_translations) summary["characterisation_dose"] = self.characterisation_dose summary["dose_per_repetition"] = self.acquisition_dose - - summary["total_radiation_dose"] = ( - summary["dose_per_repetition"] * summary["repetition_count"] - + summary["characterisation_dose"] - ) + summary["total_radiation_dose"] = self.total_radiation_dose summary["total_dose_budget"] = self.recommended_dose_budget() return summary @@ -2210,11 +2278,15 @@ def set_pre_strategy_params( resolution, orgxy=orgxy, Distance=distance ) + maximum_chi = settings["maximum_chi"] + maximum_chi_from_limits = HWR.beamline.gphl_workflow.derive_maximum_chi() + if maximum_chi_from_limits: + maximum_chi = min(maximum_chi, maximum_chi_from_limits) self.strategy_options = { "strategy_type": self.strategy_type, "angular_tolerance": settings["angular_tolerance"], "clip_kappa": settings["angular_tolerance"], - "maximum_chi": settings["maximum_chi"], + "maximum_chi": maximum_chi, } for tag in ("allow_duplicate_orientations", "delphi_block", "stratcal_step"): if tag in settings: @@ -2228,15 +2300,17 @@ def set_pre_strategy_params( or self.strategy_settings["variants"][0] ) if self.characterisation_done: - self.strategy_options["variant"] = self.strategy_variant = strategy_variant + self.strategy_options["variant"] = strategy_variant elif self.wftype == "diffractcal": - self.strategy_options["variant"] = self.strategy_variant = strategy_variant + self.strategy_options["variant"] = strategy_variant self.initial_strategy = strategy_variant elif self.wftype != "transcal": # This must be characterisation - here we do not accept defaults self.initial_strategy = ( strategy or settings["characterisation_strategies"][0] ) + if not self.tracking_data.workflow_name: + self.tracking_data.workflow_name = self.workflow_name # NB init_spot_dir must be re-set every time, hence no if test self.init_spot_dir = init_spot_dir @@ -2345,8 +2419,9 @@ def init_from_task_data(self, sample_model, params): ) if not self.strategy_settings: raise ValueError( - "No GΦL workflow strategy named %s found" % params["strategy_name"] + "No GPhL workflow strategy named %s found" % params["strategy_name"] ) + self.tracking_data.workflow_type = self.strategy_type self.shape = params.get("shape", "") for tag in ( @@ -2490,7 +2565,7 @@ def wftype(self): @property def wfname(self): - """ "Workflow full name, e.g. "GΦL Diffractometer calibration" """ + """ "Workflow full name, e.g. "GPhL Diffractometer calibration" """ return self.strategy_settings["wfname"] @property @@ -2503,6 +2578,21 @@ def strategy_name(self): """ "Strategy full name, e.g. "Two-wavelength MAD" """ return self.strategy_settings["title"] + @property + def strategy_short_name(self): + """ "Strategy short name, e.g. "2wvlMAD" """ + return self.strategy_settings.get("short_name") or self.strategy_type + + @property + def strategy_variant(self): + """Strategy variant""" + return self.strategy_options.get("variant") or "no_variant" + + @property + def workflow_name(self): + """ "Full workflow name, for use e.g. for MXLIMS,experiment_strategy """ + return ".".join(("gphl", self.strategy_short_name, self.strategy_variant)) + # Run name equal to base_prefix def get_name(self): # Required to conform to TaskNode @@ -2535,6 +2625,13 @@ def total_strategy_length(self): result *= len(energy_tags) return result + @property + def total_radiation_dose(self): + """Total radiation dose for entire workflow""" + return ( + self.acquisition_dose * self.repetition_count + self.characterisation_dose + ) + def calc_maximum_dose(self, energy=None, exposure_time=None, image_width=None): """Dose at transmission=100 for given energy, exposure time and image width diff --git a/mxcubecore/queue_entry/advanced_connector.py b/mxcubecore/queue_entry/advanced_connector.py index 11f0d1e897..202525661a 100644 --- a/mxcubecore/queue_entry/advanced_connector.py +++ b/mxcubecore/queue_entry/advanced_connector.py @@ -58,7 +58,7 @@ def execute(self): # gevent.sleep(2) logging.getLogger("user_level_log").info("Rotating 90 degrees") - HWR.beamline.diffractometer.move_omega_relative(90) + HWR.beamline.diffractometer.omega.set_value_relative(90) logging.getLogger("user_level_log").info("Creating a helical line") gevent.sleep(2) diff --git a/mxcubecore/queue_entry/base_queue_entry.py b/mxcubecore/queue_entry/base_queue_entry.py index 503503e753..878f84d393 100644 --- a/mxcubecore/queue_entry/base_queue_entry.py +++ b/mxcubecore/queue_entry/base_queue_entry.py @@ -22,13 +22,17 @@ execute queue entries in a hierarchical manner. """ +from __future__ import annotations + import copy import logging import sys import time import traceback from collections import namedtuple +from datetime import datetime, timezone from enum import Enum +from typing import TYPE_CHECKING, Optional import gevent @@ -39,12 +43,15 @@ CENTRING_METHOD, EXPERIMENT_TYPE, ) +from mxcubecore.utils import mxutils + +if TYPE_CHECKING: + from mxlims.mxpydantic.objects.MxExperiment import MxExperiment __credits__ = ["MXCuBE collaboration"] __license__ = "LGPLv3+" __category__ = "General" - status_list = ["SUCCESS", "WARNING", "FAILED", "SKIPPED", "RUNNING", "NOT_EXECUTED"] QueueEntryStatusType = namedtuple("QueueEntryStatusType", status_list) QUEUE_ENTRY_STATUS = QueueEntryStatusType(0, 1, 2, 3, 4, 5) @@ -178,11 +185,11 @@ def swap(self, queue_entry_a, queue_entry_b): Throws a ValueError if one of the entries does not exist in the queue. - :param queue_entry: Queue entry to swap - :type queue_entry: QueueEntry + :param queue_entry_a: Queue entry to swap + :type queue_entry_a: QueueEntry - :param queue_entry: Queue entry to swap - :type queue_entry: QueueEntry + :param queue_entry_b: Queue entry to swap + :type queue_entry_b: QueueEntry """ index_a = None index_b = None @@ -222,7 +229,6 @@ def set_queue_controller(self, queue_controller): def get_queue_controller(self): """ :returns: The queue controller - :type queue_controller: QueueController """ return self._queue_controller @@ -260,6 +266,9 @@ def __init__(self, view=None, data_model=None, view_set_queue_entry=True): self.type_str = "" self._data_model.lims_session_id = HWR.beamline.session.session_id + # MXLIMS record for currently running experiment + self._mxlims_job: Optional[MxExperiment] = None + def is_failed(self): """Returns True if failed""" return self.status == QUEUE_ENTRY_STATUS.FAILED @@ -332,6 +341,17 @@ def set_enabled(self, state): """ self._checked_for_exec = state + def get_mxlims_job(self) -> Optional[MxExperiment]: + """Get MxExperiment MXLIMS record if the entry is currently running""" + obj = self + result = None + container = obj.get_container() + while result is None and container is not None: + result = obj._mxlims_job + obj = container + container = obj.get_container() + return result + def execute(self): """ Execute method, should be overridden my subclasses, defines @@ -369,6 +389,12 @@ def post_execute(self): self.get_data_model().set_enabled(False) self.set_enabled(False) + mxlims_job = self._mxlims_job + if mxlims_job is not None: + self._mxlims_job = None + mxlims_job.end_time = datetime.now(timezone.utc) + mxutils.export_mxjob(mxlims_job, None) + # self._set_background_color() def _set_background_color(self): diff --git a/mxcubecore/queue_entry/data_collection.py b/mxcubecore/queue_entry/data_collection.py index 21f84fc12a..81ab4bfd23 100644 --- a/mxcubecore/queue_entry/data_collection.py +++ b/mxcubecore/queue_entry/data_collection.py @@ -17,9 +17,14 @@ # along with MXCuBE. If not, see . import logging +import uuid +from typing import TYPE_CHECKING import gevent +if TYPE_CHECKING: + from mxlims.mxpydantic.objects.MxExperiment import MxExperiment + from mxcubecore import HardwareRepository as HWR from mxcubecore.dispatcher import dispatcher from mxcubecore.model import queue_model_objects @@ -34,6 +39,7 @@ QueueExecutionException, center_before_collect, ) +from mxcubecore.utils import mxutils __credits__ = ["MXCuBE collaboration"] __license__ = "LGPLv3+" @@ -128,11 +134,55 @@ def pre_execute(self): data_model = self.get_data_model() + mx_experiment: MxExperiment = self.get_mxlims_job() + if mx_experiment is None: + tracking_data = data_model.tracking_data + workflow_parameters = data_model.workflow_parameters + tracking_data.workflow_uid = workflow_parameters.get("workflow_uid") + tracking_data.uuid = tracking_data.workflow_uid or uuid.uuid1() + tracking_data.workflow_name = workflow_parameters.get("workflow_name") + tracking_data.workflow_type = ( + workflow_parameters.get("workflow_type") or data_model.experiment_type + ) + tracking_data.location_id = workflow_parameters.get("workflow_position_id") + # NB first orientation only: + tracking_data.orientation_id = workflow_parameters.get( + "workflow_kappa_settings_id" + ) + tracking_data.characterisation_id = workflow_parameters.get( + "characterisation_id" + ) + self._mxlims_job, mxlims_sample = mxutils.make_mx_experiment( + sample=data_model.get_sample_node(), + tracking_data=tracking_data, + measured_flux=HWR.beamline.flux.get_value(), + ) + if data_model.get_parent(): gid = data_model.get_parent().lims_group_id data_model.lims_group_id = gid def post_execute(self): + # Done in post_execute and *before* calling BaseQueueEntry + # so that beamline values are set and can be read off + # NBNB TODO look at pre-existing sweep UUIDs + detector = HWR.beamline.detector + # NB Detector distance is taken here rather than from parameters as a more + # reliable source and in preference to the definition-dependent resolution + beam_position = detector.get_beam_position() + if None in beam_position: + beam_position = None + beam = HWR.beamline.beam + data_model = self.get_data_model() + mxutils.add_data_collection( + self.get_mxlims_job(), + data_model, + beam_position=beam_position, + beam_size=beam.get_beam_size(), + beam_shape=beam.get_beam_shape().value, + detector_distance=detector.distance.get_value(), + ) + BaseQueueEntry.post_execute(self) qc = self.get_queue_controller() diff --git a/mxcubecore/utils/mxutils.py b/mxcubecore/utils/mxutils.py new file mode 100644 index 0000000000..e9db07c016 --- /dev/null +++ b/mxcubecore/utils/mxutils.py @@ -0,0 +1,341 @@ +""" + +License: + +This file is part of the MXLIMS collaboration. + +MXLIMS models and code are free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +MXLIMS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with MXLIMS. If not, see . +""" + +__copyright__ = """ Copyright © 2024 - 2024 MXLIMS collaboration.""" +__author__ = "rhfogh" +__date__ = "05/11/2024" + +import uuid +from datetime import datetime, timezone +import logging +from pathlib import Path +from typing import Optional, Tuple, Union + +from mxlims.mxpydantic.datatypes.Scan import Scan +from mxlims.mxpydantic.datatypes.UnitCell import UnitCell +from mxlims.mxpydantic.messages.MxlimsMessage import MxlimsMessage +from mxlims.mxpydantic.objects.CollectionSweep import CollectionSweep +from mxlims.mxpydantic.objects.Crystal import Crystal +from mxlims.mxpydantic.objects.MacromoleculeSample import MacromoleculeSample +from mxlims.mxpydantic.objects.MxExperiment import MxExperiment +from mxlims.mxpydantic.objects.MxProcessing import MxProcessing + +from mxcubecore.model import queue_model_objects as qmo + + +def make_mx_experiment( # noqa: C901, PLR0912, PLR0915 + sample: qmo.Sample, + tracking_data: qmo.TrackingData, + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + job_status: Optional[str] = None, + **parameters, +) -> Tuple[MxExperiment, MacromoleculeSample]: + """Create MxExperiment record from datamodel + + Args: + sample: QueueModelObject representing sample + tracking_data: Dictionary with uuid etc. connecting sweeps and workflows + start_time: Experiment start time + end_time: Experiment end time + job_status: Job status (enumerated string) + **parameters: dict of parameters overriding/supplementing MxExperimentData + + Returns: + + """ + if end_time: + end_time = end_time.astimezone(timezone.utc) + if start_time: + start_time = start_time.astimezone(timezone.utc) + else: + start_time = datetime.now(timezone.utc) + crystal = sample.crystals[0] if sample.crystals else None + diffraction_plan = sample.diffraction_plan + sampledata = { + "name": sample.name or sample.get_name() or (crystal and crystal.acronym), + } + + jobdata = { + "start_time": start_time, + "end_time": end_time, + "job_status": job_status, + "uuid": tracking_data.uuid, + } + workflow_name = tracking_data.workflow_name + if diffraction_plan and not workflow_name: + if hasattr(diffraction_plan, "experimentType"): + workflow_name = diffraction_plan.experimentType + else: + workflow_name = diffraction_plan.get("experimentType") + if not workflow_name: + workflow_name = parameters.pop("experiment_type", None) + + jobdata["experiment_strategy"] = workflow_name + + if diffraction_plan: + # It is not clear if diffraction_plan is a dict or an object, + # and if so which kind + + if hasattr(diffraction_plan, "aimedResolution"): + resolution = diffraction_plan.aimedResolution + else: + resolution = diffraction_plan.get("aimedResolution") + if resolution: + jobdata["expected_resolution"] = resolution + + if hasattr(diffraction_plan, "requiredCompleteness"): + completeness = diffraction_plan.requiredCompleteness + else: + completeness = diffraction_plan.get("requiredCompleteness") + if completeness: + jobdata["target_completeness"] = completeness + + if hasattr(diffraction_plan, "requiredMultiplicity"): + multiplicity = diffraction_plan.requiredMultiplicity + else: + multiplicity = diffraction_plan.get("requiredMultiplicity") + if multiplicity: + jobdata["target_multiplicity"] = multiplicity + jobdata.update(parameters) + + # CrystallographicSample + if crystal: + space_group_name = crystal.space_group + if space_group_name: + jobdata["expected_space_group_name"] = space_group_name + unit_cell = make_unit_cell( + crystal.cell_a, + crystal.cell_b, + crystal.cell_c, + crystal.cell_alpha, + crystal.cell_beta, + crystal.cell_gamma, + ) + if unit_cell: + jobdata["expected_unit_cell"] = unit_cell + + # Set parameters from diffraction plan + if diffraction_plan: + # It is not clear if diffraction_plan is a dict or an object, + # and if so which kind + if hasattr(diffraction_plan, "radiationSensitivity"): + radiation_sensitivity = diffraction_plan.radiationSensitivity + else: + radiation_sensitivity = diffraction_plan.get("radiationSensitivity") + if radiation_sensitivity: + jobdata["radiation_sensitivity"] = radiation_sensitivity + + sample = MacromoleculeSample(uuid=uuid.uuid1(), **sampledata) + + if crystal: + # Crystal.uuid is unfortunately not a uuid, but a name string + crystal_name = crystal.crystal_uuid + if crystal_name: + mxlims_crystal = Crystal( + uuid=uuid.uuid1(), sample_id=sample.uuid, name=crystal_name + ) + jobdata["logistical_sample_id"] = mxlims_crystal.uuid + jobdata["sample_id"] = sample.uuid + experiment = MxExperiment(**jobdata) + return experiment, sample + +def make_unit_cell(a, b, c, alpha, beta, gamma): + """Make UnitCell object""" + dd1 = { + "a": a, + "b": b, + "c": c, + "alpha": alpha, + "beta": beta, + "gamma": gamma, + } + unit_cell = UnitCell(**dd1) if all(dd1.values()) else None + return unit_cell + +def add_data_collection( + mx_experiment: MxExperiment, + data_collection: qmo.DataCollection, + **parameters: dict, +) -> Optional[CollectionSweep]: + """Make CollectionSweep record from DataCollection + + Args: + mx_experiment: container MxExperimentMessage + data_collection: DataCollection queue_model_object to add + **parameters: dict of parameters overriding/supplementing MxlimsData + + Returns: + + """ + scan_axis = "omega" + + acquisition = data_collection.acquisitions[0] + path_template = acquisition.path_template + acqparams = acquisition.acquisition_parameters + tracking_data = data_collection.tracking_data + startpos = dict( + tpl + for tpl in acqparams.centred_position.as_dict().items() + if tpl[1] is not None + ) + axis_pos_start = acqparams.osc_start + axis_pos_end = axis_pos_start + acqparams.num_images * acqparams.osc_range + startpos[scan_axis] = axis_pos_start + startpos["detector_distance"] = acqparams.detector_distance + detector_distance = parameters.pop("detector_distance", None) + if detector_distance is not None: + startpos["detector_distance"] = detector_distance + scan = Scan( + scan_position_start=axis_pos_start, + first_image_number=acqparams.first_image, + number_images=acqparams.num_images, + ordinal=tracking_data.scan_number or 0, + ) + + sweep_id = tracking_data.sweep_id + sweep = None + for dataset in mx_experiment.results: + if str(dataset.uuid) == sweep_id: + sweep = dataset + break + if sweep: + # This is a scan for an existing sweep. Add and update + sweep.scans.append(scan) + sweep.axis_positions_start[scan_axis] = min( + sweep.axis_positions_start[scan_axis], + axis_pos_start, + ) + sweep.axis_positions_end[scan_axis] = max( + sweep.axis_positions_end[scan_axis], + axis_pos_end, + ) + # No new Collection Sweep made + return None + + else: # noqa: RET505 + sweepdata = { + "uuid": sweep_id or tracking_data.uuid, + "source_id": mx_experiment.uuid, + "logistical_sample_id": mx_experiment.logistical_sample_id, + "role": tracking_data.role, + "scan_axis": scan_axis, + "exposure_time": acqparams.exp_time, + "image_width": acqparams.osc_range, + "energy": acqparams.energy, + "transmission": acqparams.transmission, + "resolution": acqparams.resolution, + "detector_binning_mode": acqparams.detector_binning_mode, + "detector_roi_mode": acqparams.detector_roi_mode, + "overlap": acqparams.overlap, + "number_triggers": acqparams.num_triggers, + "number_images_per_trigger": acqparams.num_images_per_trigger, + "prefix": path_template.get_prefix(), + "file_type": path_template.suffix, + "filename_template": path_template.get_image_file_name(), + "path": path_template.directory, + "axis_positions_start": startpos, + "scans": [scan], + "axis_positions_end": {scan_axis: axis_pos_end}, + } + + # NBNB how do we get the detector type? + # NBNB do we use MXCuBE axis names or standardised names? + + sweepdata.update(parameters) + return CollectionSweep(**sweepdata) + + +def export_mxjob( # noqa: C901 + mxlims_job: Union[MxExperiment, MxProcessing], + path_template: Optional[qmo.PathTemplate] = None, +): + """Export MxExperiment mxlims record with linked objects to JSON file""" + if path_template: + template = "MXExperiment_%s_%s.json" + file_name = template % (path_template.get_prefix(), path_template.run_number) + path = Path(path_template.directory) / file_name + else: + path = None + + jobs = [mxlims_job] + objects_by_uuid = {} + for job in jobs: + objects_by_uuid[job.uuid] = job + jobs.extend(job.subjobs) + for obj in job.results: + objects_by_uuid[obj.uuid] = obj + path = Path(obj.path) / "MxExperiment.json" + for tag in ("template_data", "reference_data"): + for obj in getattr(job, tag): + objects_by_uuid[obj.uuid] = obj + if hasattr(job, "input_data"): + for obj in job.input_data: + objects_by_uuid[obj.uuid] = obj + for obj in list(objects_by_uuid.values()): + sample = getattr(obj, "sample", None) + if sample is not None: + objects_by_uuid[sample.uuid] = sample + logistical_sample = getattr(obj, "logistical_sample", None) + if logistical_sample is not None: + objects_by_uuid[logistical_sample.uuid] = logistical_sample + sample = getattr(logistical_sample, "sample", None) + if sample is not None: + objects_by_uuid[sample.uuid] = sample + if path is None: + logging.getLogger("user_level_log").debug( + "Job has no results; MXLIMS not exported" + ) + else: + message = MxlimsMessage.from_pydantic_objects(list(objects_by_uuid.values())) + print("WRITING MXLIMS JSON TO", path) # noqa: T201 + message.export_message(path) + + +if __name__ == "__main__": + # Test file loading + from argparse import ArgumentParser, RawTextHelpFormatter + + parser = ArgumentParser( + prog="generate_mxlims.py", + formatter_class=RawTextHelpFormatter, + prefix_chars="--", + description=""" +MXLIMS code generation. Assumes standard directory structure""", + ) + + parser.add_argument( + "--filename", + metavar="filename", + default=None, + help="Input file\n", + ) + + argsobj = parser.parse_args() + options_dict = vars(argsobj) + + message = MxlimsMessage.from_message_file(Path(options_dict["filename"])) + text = message.model_dump_json( + indent=4, + by_alias=True, + exclude_none=True, + serialize_as_any=True, + ) + Path(options_dict["filename"] + "_out").write_text(text) diff --git a/pyproject.toml b/pyproject.toml index 7ccb479280..e7d4fca158 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,8 @@ matplotlib = "^3.6.2" numpy = "^1.23.5" Pillow = "^10.4.0" pydantic = ">=2.8.2,<2.9.0" +datamodel-code-generator = ">= 0.28.0" +json-schema-for-humans = ">= 1.3.4" PyDispatcher = "^2.0.6" "ruamel.yaml" = "^0.17.21" scipy = "^1.9.3" diff --git a/ruff.toml b/ruff.toml index 52024d3d04..a9f7392b9d 100644 --- a/ruff.toml +++ b/ruff.toml @@ -10,6 +10,7 @@ ignore = [ "ANN", # flake8-annotations: ignored, too many hits (21744) "COM812", # Ruff recommends to ignore this linter rule when using its formatter "D", # pydocstyle: ignored, too many hits (16292) + "ICN001", # Widely flouted, and anyway of minor importance. Whoever activates it should also fix teh code "N818", # error-suffix-on-exception-name: rather pointless outside of Python itself "N999", # invalid-module-name: ignored since we have many modules with `PascalCase` "PLR2004", # magic-value-comparison: ignored, too many hits (249) @@ -173,7 +174,6 @@ convention = "google" "B904", "FBT003", "FIX002", - "ICN001", "N806", "N813", "N815", @@ -603,9 +603,6 @@ convention = "google" "SIM118", "TRY002", ] -"mxcubecore/HardwareObjects/Bliss.py" = [ - "ICN001", -] "mxcubecore/HardwareObjects/BlissActuator.py" = [ "ARG002", "B028", @@ -712,7 +709,6 @@ convention = "google" "E741", "FBT002", "G002", - "ICN001", "N802", "N803", "N806", @@ -1258,7 +1254,6 @@ convention = "google" "FBT002", "FBT003", "G002", - "ICN001", "ISC003", "PLR0912", "PLR0915", @@ -1854,7 +1849,6 @@ convention = "google" "FIX004", "G002", "G003", - "ICN001", "ISC003", "PLR0911", "PLR0912", @@ -2357,7 +2351,6 @@ convention = "google" "ERA001", "FBT002", "FBT003", - "ICN001", "N802", "N806", "PLR0913", @@ -2519,7 +2512,6 @@ convention = "google" "FBT003", "FIX002", "G002", - "ICN001", "N802", "N803", "N806", @@ -2929,7 +2921,6 @@ convention = "google" "FBT003", "FLY002", "G002", - "ICN001", "N802", "N806", "N815", @@ -3292,7 +3283,6 @@ convention = "google" "ERA001", "F821", "G002", - "ICN001", "N802", "N803", "N815", @@ -4264,7 +4254,6 @@ convention = "google" "mxcubecore/HardwareObjects/mockup/OnlineProcessingMockup.py" = [ "C901", "FBT003", - "ICN001", "NPY002", "RET508", "SIM113", @@ -4291,7 +4280,6 @@ convention = "google" ] "mxcubecore/HardwareObjects/mockup/PlottingMockup.py" = [ "ARG002", - "ICN001", "NPY002", ] "mxcubecore/HardwareObjects/mockup/ProcedureMockup.py" = [ @@ -4338,7 +4326,6 @@ convention = "google" "BLE001", "ERA001", "G002", - "ICN001", "N802", "N806", "NPY002", @@ -4363,7 +4350,6 @@ convention = "google" "F841", "FBT002", "G002", - "ICN001", "N802", "N803", "N806", @@ -4418,7 +4404,6 @@ convention = "google" "C901", "F841", "FBT002", - "ICN001", "N813", "PLR0912", "PLR0913", @@ -4664,7 +4649,6 @@ convention = "google" "ERA001", "F403", "F405", - "ICN001", "N802", "N803", "N815", diff --git a/test/test_diffractometer.py b/test/test_diffractometer.py index f5217fa3c2..5a5d41a461 100644 --- a/test/test_diffractometer.py +++ b/test/test_diffractometer.py @@ -65,10 +65,10 @@ def test_get_phase_list(self, test_object): # subtract one for the UNKNOWN assert len(phase_enum) - 1 == len(test_object.get_phase_list()) - def test_get_head_type(self, test_object): - head_enum = test_object.get_head_enum + def test_head_type(self, test_object): + head_enum = test_object.head_enum # in the mockup we set the head to be minikappa - assert test_object.get_head_type == head_enum.MINI_KAPPA + assert test_object.head_type == head_enum.MINI_KAPPA assert test_object.in_kappa_mode assert not test_object.in_plate_mode