diff --git a/mxcubecore/HardwareObjects/DESY/Centring.py b/mxcubecore/HardwareObjects/DESY/Centring.py index f195cc1e57..84f4f58bdd 100644 --- a/mxcubecore/HardwareObjects/DESY/Centring.py +++ b/mxcubecore/HardwareObjects/DESY/Centring.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with MXCuBE. If not, see . -__copyright__ = """ Copyright © 2010 - 2020 by MXCuBE Collaboration """ +__copyright__ = """ Copyright © 2010 - 2024 by MXCuBE Collaboration """ __license__ = "LGPLv3+" diff --git a/mxcubecore/HardwareObjects/DESY/MjpgStreamVideo.py b/mxcubecore/HardwareObjects/DESY/MjpgStreamVideo.py index 310bb967a6..4fe19d06f8 100644 --- a/mxcubecore/HardwareObjects/DESY/MjpgStreamVideo.py +++ b/mxcubecore/HardwareObjects/DESY/MjpgStreamVideo.py @@ -968,7 +968,7 @@ def _do_imagePolling(self, sleep_time): # image.setOffset(QPoint(300,300)) # self.image = QPixmap.fromImage(image) self.image = QPixmap.fromImage( - image.scaled(self.display_width, self.display_height) + image.scaled(int(self.display_width), int(self.display_height)) ) self.emit("imageReceived", self.image) # gevent.sleep(0.1) diff --git a/mxcubecore/HardwareObjects/DESY/P11AlbulaView.py b/mxcubecore/HardwareObjects/DESY/P11AlbulaView.py index 4062d1db26..8d51ebb028 100644 --- a/mxcubecore/HardwareObjects/DESY/P11AlbulaView.py +++ b/mxcubecore/HardwareObjects/DESY/P11AlbulaView.py @@ -18,7 +18,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with MXCuBE. If not, see . -__copyright__ = """ Copyright © 2010 - 2023 by MXCuBE Collaboration """ +from mxcubecore.BaseHardwareObjects import HardwareObject + +__copyright__ = """ Copyright © 2010 - 2024 by MXCuBE Collaboration """ __license__ = "LGPLv3+" @@ -26,10 +28,8 @@ import time from PIL import Image - sys.path.append("/opt/dectris/albula/4.0/python/") -from mxcubecore.BaseHardwareObjects import HardwareObject try: from dectris import albula @@ -99,7 +99,7 @@ def start(self, path=None, filetype=None, interval=None, stream=False): self.eigerThread.clearMonitorBuffer() self.eigerThread.setMonitorDiscardNew(True) - super(LiveView, self).start() + super().start() def stop(self, interval=0.0): diff --git a/mxcubecore/HardwareObjects/DESY/P11BackLight.py b/mxcubecore/HardwareObjects/DESY/P11BackLight.py index 8371619b50..2dc8117446 100644 --- a/mxcubecore/HardwareObjects/DESY/P11BackLight.py +++ b/mxcubecore/HardwareObjects/DESY/P11BackLight.py @@ -53,7 +53,7 @@ class P11BackLight(AbstractNState): def __init__(self, name): - super(AbstractNState, self).__init__(name) + super().__init__(name) self.cmd_open_close = None self.cmd_started = 0 @@ -72,7 +72,7 @@ def init(self): self.chan_value.connect_signal("update", self.update_light_state) self.update_light_state(self.chan_value.get_value()) - super(AbstractNState, self).init() + super().init() def get_value(self): return self.update_light_state() diff --git a/mxcubecore/HardwareObjects/DESY/P11Beam.py b/mxcubecore/HardwareObjects/DESY/P11Beam.py new file mode 100644 index 0000000000..318911d1fa --- /dev/null +++ b/mxcubecore/HardwareObjects/DESY/P11Beam.py @@ -0,0 +1,107 @@ +# Project: MXCuBE +# https://github.com/mxcube +# +# This file is part of MXCuBE software. +# +# MXCuBE is 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. +# +# MXCuBE 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 MXCuBE. If not, see . + +"""P11Beam""" + +from mxcubecore.HardwareObjects.abstract.AbstractBeam import AbstractBeam +from mxcubecore.BaseHardwareObjects import HardwareObjectState + + +__credits__ = ["DESY P11"] +__license__ = "LGPLv3+" +__category__ = "General" + + +class P11Beam(AbstractBeam): + def __init__(self, *args): + super().__init__(*args) + + self._beam_size_dict = {"aperture": [9999, 9999], "slits": [9999, 9999]} + + self._beam_position_on_screen = [340, 256] + + self.focus_sizes = { + -1: {"label": "unknown", "size": (0.2, 0.2)}, + 0: {"label": "flat", "size": (0.2, 0.2)}, + 1: {"label": "200x200", "size": (0.2, 0.2)}, + 2: {"label": "100x100", "size": (0.1, 0.1)}, + 3: {"label": "50x50", "size": (0.05, 0.05)}, + 4: {"label": "20x20", "size": (0.02, 0.02)}, + 5: {"label": "4x9", "size": (0.009, 0.004)}, + } + + def init(self): + self.mirror_idx_ch = self.get_channel_object("beamsize") + self.mirror_state_ch = self.get_channel_object("state") + + if self.mirror_idx_ch is not None: + self.mirror_idx_ch.connect_signal("update", self.mirror_idx_changed) + if self.mirror_state_ch is not None: + self.mirror_state_ch.connect_signal("update", self.mirror_state_changed) + + self.mirror_idx_changed() + self.mirror_state_changed() + + def get_beam_info_state(self): + if self.mirror_state_ch is not None: + tango_state = self.mirror_state_ch.get_value() + return self._convert_tango_state(tango_state) + + return self.STATES_READY + + def get_slits_gap(self): + return None, None + + def mirror_state_changed(self, state=None): + + if state is None: + state = self.get_beam_info_state() + + self.update_state(self._convert_tango_state(state)) + + def _convert_tango_state(self, state): + str_state = str(state) + + if str_state == "ON": + _state = self.STATES.READY + elif str_state == "MOVING": + _state = self.STATES.BUSY + else: + _state = self.STATES.FAULT + return _state + + def mirror_idx_changed(self, value=None): + + if value is None: + value = self.mirror_idx_ch.get_value() + + self.log.debug(f"P11Beam - mirror idx changed. now is {value}") + + if value not in self.focus_sizes: + value = -1 + self.log.debug(f" - UNKNOWN mirror index") + + curr_size_item = self.focus_sizes[value] + self.log.debug( + f" current mirror focus is {curr_size_item['label']}: {curr_size_item['size']}" + ) + + self._beam_size_dict["aperture"] = curr_size_item["size"] + + self.evaluate_beam_info() + self.re_emit_values() diff --git a/mxcubecore/HardwareObjects/DESY/P11Collect.py b/mxcubecore/HardwareObjects/DESY/P11Collect.py index 3171a52753..9cac094595 100644 --- a/mxcubecore/HardwareObjects/DESY/P11Collect.py +++ b/mxcubecore/HardwareObjects/DESY/P11Collect.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with MXCuBE. If not, see . -__copyright__ = """ Copyright © 2010 - 2023 by MXCuBE Collaboration """ +__copyright__ = """ Copyright © 2010 - 2024 by MXCuBE Collaboration """ __license__ = "LGPLv3+" @@ -39,24 +39,39 @@ from mxcubecore.Command.Tango import DeviceProxy +import gevent +import time +import numpy as np +import logging +import os +import sys +import h5py import triggerUtils +from tango import DeviceProxy, DevState FILE_TIMEOUT = 5 class P11Collect(AbstractCollect): def __init__(self, *args): - super(P11Collect, self).__init__(*args) + super().__init__(*args) + + def init(self): + + super().init() + + # os.system("/opt/xray/bin/adxv -socket -colors Gray -rings &") + + # os.system("/bin/bash /gpfs/local/shared/MXCuBE/STRELA/start_viewer_zmq.sh") self.default_speed = self.get_property("omega_default_speed", 130) - self.turnback_time = self.get_property("turnback_time", 0.1) + self.turnback_time = self.get_property("turnback_time", 0.3) self.filter_server_name = self.get_property("filterserver") self.mono_server_name = self.get_property("monoserver") self.filter_server = DeviceProxy(self.filter_server_name) self.mono_server = DeviceProxy(self.mono_server_name) - self.diffr = HWR.beamline.diffractometer self.lower_bound_ch = self.get_channel_object("acq_lower_bound") self.upper_bound_ch = self.get_channel_object("acq_upper_bound") @@ -66,18 +81,6 @@ def __init__(self, *args): self.acq_off_cmd = self.get_command_object("acq_off") self.acq_window_off_cmd = self.get_command_object("acq_window_off") - self.latest_frames = 1 - self.acq_speed = 1.0 - self.init_ok = False - self.latest_h5_filename = "TEST_master.h5" - - def init(self): - super(P11Collect, self).init() - - # os.system("/opt/xray/bin/adxv -socket -colors Gray -rings &") - - # os.system("/bin/bash /gpfs/local/shared/MXCuBE/STRELA/start_viewer_zmq.sh") - if None in [ self.lower_bound_ch, self.upper_bound_ch, @@ -124,6 +127,9 @@ def data_collection_hook(self): "P11Collect. - object initialization failed. COLLECTION not possible" ) + osc_pars["kappa"] = 0 + osc_pars["kappa_phi"] = 0 + self.diffr = HWR.beamline.diffractometer detector = HWR.beamline.detector @@ -167,13 +173,6 @@ def data_collection_hook(self): if not ret: raise BaseException("Cannot set prepare collection . Aborting") - # writeInfo (is it necessary?) - - # # Separate_instance of the modified Dectris pyqt Viewer. - # print("=============== STARTING THE VIEWER ===============") - # new_gui = subprocess.Popen("/bin/bash /gpfs/local/shared/MXCuBE/STRELA/start_viewer.sh") - # print("=============== FINISHED STARTING THE VIEWER ===============") - try: self.log.debug("############# #COLLECT# Opening detector cover") self.diffr.detector_cover_open(wait=True) @@ -201,6 +200,11 @@ def data_collection_hook(self): # Filepath to the EDNA processing # filepath = os.path.join(basepath,"%s_%d" % (prefix, runno)) + # setting up xds_dir for characterisation (used there internally to create dirs) + self.current_dc_parameters["xds_dir"] = os.path.join( + basepath, "%s_%d" % (prefix, runno) + ) + self.log.debug( "======= CURRENT FILEPATH: " + str(filepath) @@ -213,16 +217,6 @@ def data_collection_hook(self): + "=======================================" ) - # # === if folder exists - increase run number - # print("========CHECKING EXISTING DIRECTORY ===========") - # print(os.path.exists(self.latest_h5_filename)) - # if os.path.exists(self.latest_h5_filename): - # print("======= File exists! Increasing run number ===========") - # self.current_dc_parameters["fileinfo"]["run_number"]=self.current_dc_parameters["fileinfo"]["run_number"]+1 - # print("Run number has changed to ", self.current_dc_parameters["fileinfo"]["run_number"]) - # runno = self.current_dc_parameters["fileinfo"]["run_number"] - # filepath = os.path.join(basepath,prefix,"screening_"+str(runno).zfill(3)+"/"+"%s_%d" % (prefix, runno)) - self.log.debug( "======= CURRENT FILEPATH: " + str(filepath) @@ -235,7 +229,7 @@ def data_collection_hook(self): + "=======================================" ) - # overlap = osc_pars["overlap"] + overlap = osc_pars["overlap"] angle_inc = 90.0 detector.prepare_characterisation( exp_time, nframes, angle_inc, filepath @@ -277,58 +271,15 @@ def data_collection_hook(self): self.collect_characterisation( start_angle, img_range, nframes, angle_inc, exp_time ) - # TODO: Add LiveView here - # os.system("killall albula") - # os.system("/opt/dectris/albula/4.0/bin/albula "+self.latest_h5_filename +" &") - # os.system("adxv "+self.latest_h5_filename +" &") - - # Open index_html - os.system("firefox /gpfs/current/processed/index.html") - - # Create diffraction snapshots - for i in range(nframes): - os.system( - "python3 /gpfs/local/shared/MXCuBE/hdf5tools/albula_api/generate_image.py --input " - + self.latest_h5_filename - + " --output " - + os.path.join( - basepath, prefix, "screening_" + str(runno).zfill(3) - ) - + "/" - + " --image_number " - + str(i + 1) - ) - else: self.collect_std_collection(start_angle, stop_angle) self.generate_xds_template() - # self.adxv_notify(self.latest_h5_filename) - # TODO: Add LiveView here - # os.system("killall albula") - # os.system("/opt/dectris/albula/4.0/bin/albula "+self.latest_h5_filename +" &") - - # Open index_html - os.system("firefox /gpfs/current/processed/index.html") - - # Create diffraction snapshots - os.system( - "python3 /gpfs/local/shared/MXCuBE/hdf5tools/albula_api/generate_image.py --input " - + self.latest_h5_filename - + " --output " - + os.path.join( - basepath, prefix, "rotational_" + str(runno).zfill(3) - ) - + "/" - + " --image_number 1" - ) except RuntimeError: self.log.error(traceback.format_exc()) finally: self.acquisition_cleanup() - - # self.add_h5_info(self.latest_h5_filename) - self.trigger_auto_processing() + self.trigger_auto_processing() def collect_std_collection(self, start_angle, stop_angle): """ @@ -339,21 +290,28 @@ def collect_std_collection(self, start_angle, stop_angle): :param stop_angle: The stop_angle parameter is the final angle at which the collection should stop """ + HWR.beamline.diffractometer.wait_omega() - self.omega_mv(start_angle, self.default_speed) + start_pos = start_angle - self.turnback_time * self.acq_speed + stop_pos = stop_angle + self.turnback_time * self.acq_speed self.log.debug("#COLLECT# Running OMEGA through the std acquisition") if start_angle <= stop_angle: self.lower_bound_ch.set_value(start_angle) self.upper_bound_ch.set_value(stop_angle) + else: self.lower_bound_ch.set_value(stop_angle) self.upper_bound_ch.set_value(start_angle) - self.acq_arm_cmd() - final_pos = stop_angle + self.acq_speed * self.turnback_time - - self.omega_mv(final_pos, self.acq_speed) + self.omega_mv(start_pos, self.default_speed) + self.acq_arm_cmd() + self.omega_mv(stop_pos, self.acq_speed) + time.sleep(0.5) + self.acq_off_cmd() + self.acq_window_off_cmd() + self.omega_mv(stop_angle, self.acq_speed) + HWR.beamline.diffractometer.wait_omega() def collect_characterisation( self, start_angle, img_range, nimages, angle_inc, exp_time @@ -374,34 +332,34 @@ def collect_characterisation( diffr = HWR.beamline.diffractometer - self.log.debug("#COLLECT# Running OMEGA through the char acquisition") + self.log.debug( + "#COLLECT# Running OMEGA through the characteristation acquisition" + ) self.omega_mv(start_angle, self.default_speed) for img_no in range(nimages): - print("collecting image %s" % img_no) + logging.info("collecting image %s" % img_no) start_at = start_angle + angle_inc * img_no stop_angle = start_at + img_range * 1.0 - print("collecting image %s, angle %f" % (img_no, start_at)) + + logging.info("collecting image %s, angle %f" % (img_no, start_at)) if start_at >= stop_angle: - init_pos = start_at - self.acq_speed * self.turnback_time - # init_pos = start_at - 1.5 + init_pos = start_at + else: - init_pos = start_at + self.acq_speed * self.turnback_time - # init_pos = start_at + 1.5 - self.omega_mv(init_pos, self.default_speed) - self.collect_std_collection(start_angle, stop_angle) + init_pos = start_at + + self.collect_std_collection(start_at, stop_angle) - diffr.set_omega_velocity(self.default_speed) - self.acq_window_off_cmd() - self.acq_off_cmd() self.log.debug( "======= collect_characterisation Waiting =======================================" ) - # Let adxv know whether it is - # self.adxv_notify(self.latest_h5_filename,img_no+1) + self.log.debug( + "======= collect_characterisation Waiting =======================================" + ) def adxv_notify(self, image_filename, image_num=1): """ @@ -415,8 +373,8 @@ def adxv_notify(self, image_filename, image_num=1): """ logging.getLogger("HWR").info(f"ADXV notify {image_filename}") logging.getLogger("HWR").info(f"ADXV notify {image_num}") - adxv_host = "localhost" # self.getProperty("adxv_host", "localhost") - adxv_port = 8100 # int(self.getProperty("adxv_port", "8100")) + adxv_host = "localhost" + adxv_port = 8100 try: adxv_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -439,80 +397,132 @@ def acquisition_cleanup(self): try: diffr = HWR.beamline.diffractometer detector = HWR.beamline.detector - diffr.set_omega_velocity(self.default_speed) - self.acq_window_off_cmd() + detector.stop_acquisition() + diffr.wait_omega() + # ================= + # It is probably already finished in a standard collection. self.acq_off_cmd() + self.acq_window_off_cmd() + # ================== + diffr.set_omega_velocity(self.default_speed) self.log.debug("#COLLECT# Closing detector cover") diffr.detector_cover_close(wait=True) - detector.stop_acquisition() + + # Move omega to 0 at the end + self.omega_mv(0, self.default_speed) + self.wait_omega() + except RuntimeError: self.log.error(traceback.format_exc()) def add_h5_info(self, h5file): """ - The function `add_h5_info` waits for a specified amount of time for a file to appear on disk and - raises an exception if the file does not appear within the timeout period. + Add information to an HDF5 file. - :param h5file: The `h5file` parameter is the name or path of the H5 file that you want to add - information to + :param h5file: The name or path of the HDF5 file. """ self.log.debug("========== Writing H5 info ==============") - h5file = self.latest_h5_filename - # wait up to 5 seconds to see the file appear - start_wait = time.time() + # Wait for the HDF5 file to appear with a timeout + start_time = time.time() while not os.path.exists(h5file): - if time.time() - start_wait > FILE_TIMEOUT: - raise RuntimeWarning( - "Cannot add info to H5 file. Timeout waiting for file on disk." + if time.time() - start_time > 5: + raise IOError( + "Cannot add info to HDF5 file. Timeout waiting for file on disk." ) time.sleep(0.5) try: - h5fd = h5py.File(h5file, "r+") - group = h5fd.create_group("entry/source") - group.attrs["NX_class"] = np.array("NXsource", dtype="S") - group.create_dataset("name", data=np.array("PETRA III, DESY", dtype="S")) - group = h5fd.get("entry/instrument") - group.create_dataset("name", data=np.array("P11", dtype="S")) - group = h5fd.create_group("entry/instrument/attenuator") - group.attrs["NX_class"] = np.array("NXattenuator", dtype="S") - - data_set = group.create_dataset( - "thickness", dtype="f8", data=self.get_filter_thickness() - ) - data_set.attrs["units"] = np.array("m", dtype="S") - data_set = group.create_dataset( - "type", data=np.array("Aluminum", dtype="S") - ) - data_set = group.create_dataset( - "attenuator_transmission", - dtype="f8", - data=self.get_filter_transmission(), - ) - # fix rotation axis and detector orientation - data_set = h5fd.get("entry/sample/transformations/omega") - data_set.attrs["vector"] = [1.0, 0.0, 0.0] - data_set = h5fd.get("entry/instrument/detector/module/fast_pixel_direction") - data_set.attrs["vector"] = [1.0, 0.0, 0.0] - data_set = h5fd.get("entry/instrument/detector/module/slow_pixel_direction") - data_set.attrs["vector"] = [0.0, 1.0, 0.0] - # delete phi angle info to avoid confusion - nodes = [ - "entry/sample/goniometer/phi", - "entry/sample/goniometer/phi_end", - "entry/sample/goniometer/phi_range_average", - "entry/sample/goniometer/phi_range_total", - ] - for node in nodes: - if node in h5fd: - del h5fd[node] - h5fd.close() - - except RuntimeError as err_msg: - self.log.debug("Error while adding info to HDF5 file (%s)" % str(err_msg)) + with h5py.File(h5file, "r+") as h5fd: + # Create or get the 'entry/source' group + source_group = self.get_or_create_group(h5fd, "entry/source") + source_group.attrs["NX_class"] = np.array("NXsource", dtype="S") + + # Create or get datasets within the 'entry/source' group + self.create_or_get_dataset( + source_group, "name", np.array("PETRA III, DESY", dtype="S") + ) + + # Create or get the 'entry/instrument' group + instrument_group = self.get_or_create_group(h5fd, "entry/instrument") + + # Create or get datasets within the 'entry/instrument' group + self.create_or_get_dataset( + instrument_group, "name", np.array("P11", dtype="S") + ) + + # Create or get the 'entry/instrument/attenuator' group + attenuator_group = self.get_or_create_group( + instrument_group, "attenuator" + ) + attenuator_group.attrs["NX_class"] = np.array("NXattenuator", dtype="S") + + # Create or get datasets within the 'entry/instrument/attenuator' group + self.create_or_get_dataset( + attenuator_group, "thickness", self.get_filter_thickness() + ) + self.create_or_get_dataset( + attenuator_group, "type", np.array("Aluminum", dtype="S") + ) + self.create_or_get_dataset( + attenuator_group, + "attenuator_transmission", + self.get_filter_transmission(), + ) + + # Set attributes for certain nodes + h5fd["entry/sample/transformations/omega"].attrs["vector"] = [ + 1.0, + 0.0, + 0.0, + ] + h5fd["entry/instrument/detector/module/fast_pixel_direction"].attrs[ + "vector" + ] = [1.0, 0.0, 0.0] + h5fd["entry/instrument/detector/module/slow_pixel_direction"].attrs[ + "vector" + ] = [0.0, 1.0, 0.0] + + # Delete unwanted nodes + unwanted_nodes = [ + "entry/sample/goniometer/phi", + "entry/sample/goniometer/phi_end", + "entry/sample/goniometer/phi_range_average", + "entry/sample/goniometer/phi_range_total", + ] + for node in unwanted_nodes: + if node in h5fd: + del h5fd[node] + except Exception as err_msg: + self.log.debug(f"Error while adding info to HDF5 file: {str(err_msg)}") self.log.debug(traceback.format_exc()) + def get_or_create_group(self, parent_group, group_name): + """ + Get or create a group within a parent group. + + :param parent_group: The parent group where the new group will be created. + :param group_name: The name of the group to get or create. + :return: The group object. + """ + if group_name in parent_group: + return parent_group[group_name] + else: + return parent_group.create_group(group_name) + + def create_or_get_dataset(self, group, dataset_name, dataset_data): + """ + Create or get a dataset within a group. + + :param group: The group where the dataset will be created or retrieved. + :param dataset_name: The name of the dataset. + :param dataset_data: The data to be stored in the dataset. + """ + if dataset_name in group: + dataset = group[dataset_name] + else: + dataset = group.create_dataset(dataset_name, data=dataset_data) + def get_filter_thickness(self): """ The function `get_filter_thickness` calculates the total thickness of three filters. @@ -526,7 +536,7 @@ def get_filter_thickness(self): thickness = int(thick1) + int(thick2) + int(thick3) - return float(thickness) / 1000000 + return float(thickness) / 1_000_000 else: return -1 @@ -670,7 +680,7 @@ def trigger_auto_processing(self, process_event=None, frame_number=None): mosflm_path_local.split("/gpfs/current/processed/")[1] + "\n" ) except: - print(sys.exc_info()) + logging.info(sys.exc_info()) # create call # btHelper.user_sshkey = btHelper.user_sshkey.replace("/gpfs/current",triggerUtils.get_beamtime_metadata()[2]) @@ -745,10 +755,6 @@ def trigger_auto_processing(self, process_event=None, frame_number=None): try: self.mkdir_with_mode(xdsapp_path_local, mode=0o777) - # f=open(xdsapp_path_local+"/xdsapp.log", 'a') - # f.write("xdsapp.log") - # f.close() - self.log.debug( "=========== XDSAPP ============ XDSAPP directory created" ) @@ -766,7 +772,7 @@ def trigger_auto_processing(self, process_event=None, frame_number=None): xdsapp_path_local.split("/gpfs/current/processed/")[1] + "\n" ) except RuntimeError: - print(sys.exc_info()) + logging.info(sys.exc_info()) # create call ssh = btHelper.get_ssh_command() @@ -808,115 +814,12 @@ def trigger_auto_processing(self, process_event=None, frame_number=None): ) ) - # imagepath = self.path.get_path( - # "/central/beamtime/raw/user/sample/rotational_number/" + - # "sample_rotational_number_master.h5") - # processpath = "/beamline/p11/current" + self.path.get_path( - # "/processed/user/sample/rotational_number/xdsapp") - # - # - # - # - # #create processing folder with 0o777 - # self.path.get_path("/beamline/beamtime/processed/user/sample/" + - # "rotational_number/xdsapp", force=True) - # #add to datasets.txt for presenterd - # try: - # f = open(self.path.get_path( - # "/beamline/beamtime/processed/datasets.txt"), "a") - # f.write(self.path.get_path( - # "user/sample/rotational_number/xdsapp\n").lstrip("/")) - # f.close() - # except: - # print(sys.exc_info()) - # - # #create call - # ssh = btHelper.get_ssh_command() - # sbatch = btHelper.get_sbatch_command( - # jobname_prefix = "xdsapp", - # job_dependency = "", - # logfile_path = processpath + "/xdsapp.log" - # ) - # cmd = ("/asap3/petra3/gpfs/common/p11/processing/xdsapp_sbatch.sh " + \ - # "{imagepath:s} {processpath:s} {res:f}").format( - # imagepath = imagepath, - # processpath = processpath, - # res = res - # ) - # print(cmd) - # os.system("{ssh:s} \"{sbatch:s} --wrap \\\"{cmd:s}\\\"\"".format( - # ssh = ssh, - # sbatch = sbatch, - # cmd = cmd - # )) - - # Test of the autoprocessing as in CC (AG) - # def trigger_auto_processing(self, process_event=None, frame_number=None): - # self.log.debug("Triggering auto processing. NOT IMPLEMENTED YET. Direct test from CC.") - # - # #creation will fail if beamtime folder, slurm reservation or - # #bl-fs mount on the compute nodes can not be found - # try: - # btHelper = triggerUtils.Trigger() - # except: - # print(sys.exc_info()) - # return - # - # energy = self.petraThread.currentMonoEnergy / 1000. - # wavelength = 12.3984 / energy #in Angstrom - # res = wavelength / (2. * math.sin(0.5 * math.atan( - # (311. / 2.) / self.parameters["detectordistance"]))) - # frames = self.parameters["frames"] - # imagepath = self.path.get_path( - # "/central/beamtime/raw/user/sample/rotational_number/" + - # "sample_rotational_number_master.h5") - # processpath = "/beamline/p11/current" + self.path.get_path( - # "/processed/user/sample/rotational_number/xdsapp") - # - # #create processing folder with 0o777 - # self.path.get_path("/beamline/beamtime/processed/user/sample/" + - # "rotational_number/xdsapp", force=True) - # #add to datasets.txt for presenterd - # try: - # f = open(self.path.get_path( - # "/beamline/beamtime/processed/datasets.txt"), "a") - # f.write(self.path.get_path( - # "user/sample/rotational_number/xdsapp\n").lstrip("/")) - # f.close() - # except: - # print(sys.exc_info()) - # - # #create call - # ssh = btHelper.get_ssh_command() - # sbatch = btHelper.get_sbatch_command( - # jobname_prefix = "xdsapp", - # job_dependency = "", - # logfile_path = processpath + "/xdsapp.log" - # ) - # cmd = ("/asap3/petra3/gpfs/common/p11/processing/xdsapp_sbatch.sh " + \ - # "{imagepath:s} {processpath:s} {res:f}").format( - # imagepath = imagepath, - # processpath = processpath, - # res = res - # ) - # print(cmd) - # os.system("{ssh:s} \"{sbatch:s} --wrap \\\"{cmd:s}\\\"\"".format( - # ssh = ssh, - # sbatch = sbatch, - # cmd = cmd - # )) - def diffractometer_prepare_collection(self): diffr = HWR.beamline.diffractometer self.log.debug("#COLLECT# preparing collection ") if not diffr.is_collect_phase(): self.log.debug("#COLLECT# going to collect phase") - # # If the pinhole is Down set pinhole to 200 - # if HWR.beamline.diffractometer.pinhole_hwobj.get_position() == "Down": - # print("Pinhole is down. Setting pinhole to 200.") - # HWR.beamline.diffractometer.pinhole_hwobj.set_position("200") - # HWR.beamline.diffractometer.wait_phase() diffr.goto_collect_phase(wait=True) self.log.debug("#COLLECT# now in collect phase: %s" % diffr.is_collect_phase()) @@ -1001,7 +904,7 @@ def get_relative_path(self, path1, path2): if path_1[i] != v__: break - parts = ["..",] * (len(path_2) - i) + parts = [".."] * (len(path_2) - i) parts.extend(path_1[i:]) return os.path.join(*parts) @@ -1043,7 +946,6 @@ def mkdir_with_mode(self, directory, mode): oldmask = os.umask(000) os.makedirs(directory, mode=mode) os.umask(oldmask) - # self.checkPath(directory,force=True) self.log.debug("local directory created") @@ -1053,7 +955,6 @@ def create_directories(self, *args): """ for directory in args: try: - # os.makedirs(directory) self.mkdir_with_mode(directory, mode=0o777) except os.error as err_: if err_.errno != errno.EEXIST: @@ -1088,21 +989,12 @@ def check_path(self, path=None, force=False): try: os.mkdir(path, mode=0o777) except RuntimeError: - print("mkdir failed:", str(sys.exc_info())) + logging.info("mkdir failed:", str(sys.exc_info())) return False else: - print("dir not found:", str(sys.exc_info())) + logging.info("dir not found:", str(sys.exc_info())) return False if not os.access(path, os.W_OK): - print("dir not writeable:", str(sys.exc_info())) + logging.info("dir not writeable:", str(sys.exc_info())) return False return path - - # def mkdir_with_mode_remote(self, directory, mode): - # ssh = btHelper.get_ssh_command() - # - # os.system("{ssh:s} \"{sbatch:s} --wrap \\\"{cmd:s}\\\"\"".format( - # ssh = ssh, - # sbatch = sbatch, - # cmd = cmd - # )) diff --git a/mxcubecore/HardwareObjects/DESY/P11DetectorCover.py b/mxcubecore/HardwareObjects/DESY/P11DetectorCover.py index e246d7646b..c37e569930 100644 --- a/mxcubecore/HardwareObjects/DESY/P11DetectorCover.py +++ b/mxcubecore/HardwareObjects/DESY/P11DetectorCover.py @@ -24,7 +24,7 @@ from mxcubecore.HardwareObjects.abstract.AbstractShutter import AbstractShutter from mxcubecore.BaseHardwareObjects import HardwareObjectState -from enum import Enum, unique +from enum import Enum __credits__ = ["DESY P11"] @@ -42,19 +42,9 @@ class P11DetectorCover(AbstractShutter): default_timeout = 4 move_time_min = 1 - @unique - class BaseValueEnum(Enum): - """Defines only the compulsory values.""" - OPEN = "OPEN" - CLOSED = "CLOSED" - MOVING = "MOVING" - UNKNOWN = "UNKNOWN" - - VALUES=BaseValueEnum - def __init__(self, name): - super(AbstractShutter, self).__init__(name) + super().__init__(name) self.simulation = False self.simulated_opened = False @@ -73,6 +63,7 @@ def __init__(self, name): def init(self): """Initilise the predefined values""" + self._initialise_values() self.simulation = self.get_property("simulation") self.cmd_timeout = self.get_property("command_timeout", self.default_timeout) @@ -99,7 +90,13 @@ def init(self): else: self.simulated_update() - super(AbstractShutter, self).init() + super().init() + + def _initialise_values(self): + """Add additional, known in advance states to VALUES""" + values_dict = {item.name: item.value for item in self.VALUES} + values_dict.update({"MOVING": "MOVING"}) + self.VALUES = Enum("ValueEnum", values_dict) def get_value(self): if self.simulation: @@ -196,14 +193,8 @@ def simulated_update(self): self.update_value(value) return value - - def is_open(self): - """Check if the shutter is open. - Returns: - (bool): True if open, False otherwise. - """ - return self.get_value() == self.VALUES.OPEN + @property def is_closed(self): """Check if the shutter is closed. Returns: diff --git a/mxcubecore/HardwareObjects/DESY/P11DetectorDistance.py b/mxcubecore/HardwareObjects/DESY/P11DetectorDistance.py index e76a389649..0bf8741246 100644 --- a/mxcubecore/HardwareObjects/DESY/P11DetectorDistance.py +++ b/mxcubecore/HardwareObjects/DESY/P11DetectorDistance.py @@ -133,7 +133,7 @@ def update_value(self, value=None): if value is None: value = self.chan_position.get_value() - super(P11DetectorDistance, self).update_value(value) + super().update_value(value) def _set_value(self, value): """ @@ -141,7 +141,7 @@ def _set_value(self, value): :param value: float :return: """ - ##if self.chan_state is not None: + # if self.chan_state is not None: # self.update_state(self.STATES.BUSY) self.chan_position.set_value(value) diff --git a/mxcubecore/HardwareObjects/DESY/P11EDNACharacterisation.py b/mxcubecore/HardwareObjects/DESY/P11EDNACharacterisation.py index 7e0eb6c775..ae5c9ac5de 100644 --- a/mxcubecore/HardwareObjects/DESY/P11EDNACharacterisation.py +++ b/mxcubecore/HardwareObjects/DESY/P11EDNACharacterisation.py @@ -47,10 +47,27 @@ def __init__(self, name): self.edna_default_file = None self.start_edna_command = None + def _run_edna(self, input_file, results_file, process_directory): + """Starts EDNA""" + msg = "Starting EDNA characterisation using xml file %s" % input_file + logging.getLogger("queue_exec").info(msg) + + args = (self.start_edna_command, input_file, results_file, process_directory) + # subprocess.call("%s %s %s %s" % args, shell=True) + + # Test run DESY + self.edna_maxwell(process_directory, input_file, results_file) + + self.result = None + if os.path.exists(results_file): + self.result = XSDataResultMXCuBE.parseFile(results_file) + + return self.result + def edna_maxwell(self, process_directory, inputxml, outputxml): """ The function `edna_maxwell` is used to execute a command on a remote cluster using SSH and SBATCH. - + :param process_directory: The `process_directory` parameter is the directory where the processing will take place. It is a string that represents the path to the directory :param inputxml: The inputxml parameter is the path to the input XML file that will be used as @@ -75,21 +92,23 @@ def edna_maxwell(self, process_directory, inputxml, outputxml): + "/edna.log", ) - cmd = ( - "/asap3/petra3/gpfs/common/p11/processing/edna_sbatch.sh " - + "{inxml:s} {outxml:s} {processpath:s}" - ).format( - inxml=inputxml.replace("/gpfs", "/beamline/p11"), - outxml=outputxml.replace("/gpfs", "/beamline/p11"), - processpath=process_directory.replace("/gpfs", "/beamline/p11") + "/edna", - ) - - self.mkdir_with_mode(process_directory + "/edna", mode=0o777) - # Check path conversion inxml = inputxml.replace("/gpfs", "/beamline/p11") - outxml = outputxml.replace("/gpfs", "/beamline/p11") - processpath = process_directory.replace("/gpfs", "/beamline/p11") + "/edna" + outxml = outputxml.replace( + "/gpfs/current/raw", "/beamline/p11/current/processed" + ) + processpath = ( + process_directory.replace( + "/gpfs/current/raw", "/beamline/p11/current/processed" + ) + + "/edna" + ) + self.mkdir_with_mode( + process_directory.replace("/gpfs/current/raw", "/gpfs/current/processed/") + + "/edna", + mode=0o777, + ) + self.log.debug( '=======EDNA========== CLUSTER PROCESS DIRECTORY="%s"' % processpath ) @@ -98,6 +117,12 @@ def edna_maxwell(self, process_directory, inputxml, outputxml): self.log.debug('=======EDNA========== ssh="%s"' % ssh) self.log.debug('=======EDNA========== sbatch="%s"' % sbatch) + + cmd = ( + "/asap3/petra3/gpfs/common/p11/processing/edna_sbatch.sh " + + "{inxml:s} {outxml:s} {processpath:s}" + ).format(inxml=inxml, outxml=outxml, processpath=processpath) + self.log.debug('=======EDNA========== executing process cmd="%s"' % cmd) self.log.debug( '=======EDNA========== {ssh:s} "{sbatch:s} --wrap \\"{cmd:s}\\""'.format( @@ -105,6 +130,12 @@ def edna_maxwell(self, process_directory, inputxml, outputxml): ) ) + logging.info( + '{ssh:s} "{sbatch:s} --wrap \\"{cmd:s}"\\"'.format( + ssh=ssh, sbatch=sbatch, cmd=cmd + ) + ) + os.system( '{ssh:s} "{sbatch:s} --wrap \\"{cmd:s}"\\"'.format( ssh=ssh, sbatch=sbatch, cmd=cmd @@ -236,15 +267,24 @@ def input_from_params(self, data_collection, char_params): acquisition_parameters = data_collection.acquisitions[0].acquisition_parameters path_template = data_collection.acquisitions[0].path_template + # Make sure there is a proper path conversion between different mount points + logging.info( + "======= Characterisation path template ====%s", path_template.directory + ) + image_dir = path_template.directory.replace( "/gpfs/current", triggerUtils.get_beamtime_metadata()[2] ) + logging.info(image_dir) + path_str = os.path.join(image_dir, path_template.get_image_file_name()) + logging.info(path_template.xds_dir) characterisation_dir = path_template.xds_dir.replace( "/autoprocessing_", "/characterisation_" ) + os.makedirs(characterisation_dir, mode=0o755, exist_ok=True) for img_num in range(int(acquisition_parameters.num_images)): @@ -262,7 +302,7 @@ def input_from_params(self, data_collection, char_params): def mkdir_with_mode(self, directory, mode): """ The function creates a directory with a specified mode if it does not already exist. - + :param directory: The "directory" parameter is the path of the directory that you want to create. It can be an absolute path or a relative path :param mode: The "mode" parameter in the above code refers to the permissions that will be set diff --git a/mxcubecore/HardwareObjects/DESY/P11EigerDetector.py b/mxcubecore/HardwareObjects/DESY/P11EigerDetector.py index 0029d2a672..5bd7db6e24 100644 --- a/mxcubecore/HardwareObjects/DESY/P11EigerDetector.py +++ b/mxcubecore/HardwareObjects/DESY/P11EigerDetector.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with MXCuBE. If not, see . -__copyright__ = """ Copyright © 2010 - 2023 by MXCuBE Collaboration """ +__copyright__ = """ Copyright © 2010 - 2024 by MXCuBE Collaboration """ __license__ = "LGPLv3+" import gevent diff --git a/mxcubecore/HardwareObjects/DESY/P11Energy.py b/mxcubecore/HardwareObjects/DESY/P11Energy.py index 88b6719deb..cda97ccd68 100644 --- a/mxcubecore/HardwareObjects/DESY/P11Energy.py +++ b/mxcubecore/HardwareObjects/DESY/P11Energy.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with MXCuBE. If not, see . -__copyright__ = """ Copyright © 2010 - 2023 by MXCuBE Collaboration """ +__copyright__ = """ Copyright © 2010 - 2024 by MXCuBE Collaboration """ __license__ = "LGPLv3+" from mxcubecore.HardwareObjects.abstract.AbstractEnergy import AbstractEnergy @@ -34,7 +34,7 @@ class P11Energy(AbstractEnergy): _default_energy = 12.0 def __init__(self, name): - super(P11Energy, self).__init__(name) + super().__init__(name) def init(self): self.chan_energy = self.get_channel_object("chanEnergy") diff --git a/mxcubecore/HardwareObjects/DESY/P11FastShutter.py b/mxcubecore/HardwareObjects/DESY/P11FastShutter.py index 4b4ae4c6e5..8314dde9e4 100644 --- a/mxcubecore/HardwareObjects/DESY/P11FastShutter.py +++ b/mxcubecore/HardwareObjects/DESY/P11FastShutter.py @@ -19,7 +19,7 @@ """P11Shutter""" -from enum import Enum, unique +from enum import Enum from mxcubecore.HardwareObjects.abstract.AbstractShutter import AbstractNState from mxcubecore.BaseHardwareObjects import HardwareObjectState @@ -28,48 +28,49 @@ __license__ = "LGPLv3+" __category__ = "General" -from enum import Enum, unique +from enum import Enum from mxcubecore.HardwareObjects.abstract.AbstractShutter import AbstractShutter -@unique -class FastShutterValues(Enum): - OPEN = "Open" - CLOSED = "Closed" - - class P11FastShutter(AbstractNState): """ P11 BakcLight define interface to Tango backlight at DESY P11 """ - VALUES = FastShutterValues - default_open_time = 8 default_close_time = 3 def __init__(self, name): - super(AbstractNState, self).__init__(name) + super().__init__(name) self.chan_value = None def init(self): """Initilise the predefined values""" + self._initialise_values() self.chan_value = self.get_channel_object("value") if self.chan_value is not None: self.chan_value.connect_signal("update", self.update_fast_shutter) self.update_fast_shutter(self.chan_value.get_value()) - super(AbstractNState, self).init() + super().init() + + def _initialise_values(self): + """Add additional, known in advance states to VALUES""" + values_dict = {item.name: item.value for item in self.VALUES} + values_dict.update({"OPEN": "Open", "CLOSED": "Closed"}) + self.VALUES = Enum("ValueEnum", values_dict) def get_value(self): return self.update_fast_shutter() + @property def is_open(self): return self.get_value() == self.VALUES.OPEN + @property def is_closed(self): return self.get_value() == self.VALUES.CLOSED diff --git a/mxcubecore/HardwareObjects/DESY/P11Flux.py b/mxcubecore/HardwareObjects/DESY/P11Flux.py new file mode 100644 index 0000000000..d1475ab6f4 --- /dev/null +++ b/mxcubecore/HardwareObjects/DESY/P11Flux.py @@ -0,0 +1,72 @@ +# +# Project: MXCuBE +# https://github.com/mxcube +# +# This file is part of MXCuBE software. +# +# MXCuBE is 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. +# +# MXCuBE 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 MXCuBE. If not, see . + +from random import random +from mxcubecore.HardwareObjects.abstract.AbstractFlux import AbstractFlux + +from mxcubecore import HardwareRepository as HWR + +__credits__ = ["MXCuBE collaboration"] +__category__ = "General" + + +class P11Flux(AbstractFlux): + + # default_flux - for initialising mockup + default_flux = 5e12 + + def __init__(self, name): + AbstractFlux.__init__(self, name) + + self.measured_flux_list = [] + self.measured_flux_dict = {} + self.current_flux_dict = {} + + def init(self): + + self.measure_flux() + + def get_value(self): + """Get flux at current transmission in units of photons/s""" + + """ FLUX IS CHEETED HERE - NOWERE ELSE!""" + return self.current_flux_dict["flux"] + + def measure_flux(self): + """Measures intesity""" + beam_size = HWR.beamline.beam.get_beam_size() + transmission = HWR.beamline.transmission.get_value() + flux = self.default_flux * (1 + random()) + + self.measured_flux_list = [ + { + "size_x": beam_size[0], + "size_y": beam_size[1], + "transmission": transmission, + "flux": flux, + } + ] + + self.measured_flux_dict = self.measured_flux_list[0] + self.current_flux_dict = self.measured_flux_list[0] + + self.emit( + "fluxInfoChanged", + {"measured": self.measured_flux_dict, "current": self.current_flux_dict}, + ) diff --git a/mxcubecore/HardwareObjects/DESY/P11ISPyBClient.py b/mxcubecore/HardwareObjects/DESY/P11ISPyBClient.py index d1f259cb7a..18aaa89172 100644 --- a/mxcubecore/HardwareObjects/DESY/P11ISPyBClient.py +++ b/mxcubecore/HardwareObjects/DESY/P11ISPyBClient.py @@ -26,8 +26,6 @@ def init(self): "PROPOSAL NUMBER is %s" % self.simulated_prop_number ) - self.loginType = "user" - def update_data_collection(self, mx_collection, wait=False): mx_collection["beamline_name"] = "P11" ISPyBClient.update_data_collection(self, mx_collection, wait) @@ -40,6 +38,10 @@ def store_image(self, image_dict): self.prepare_image_for_lims(image_dict) return ISPyBClient.store_image(self, image_dict) + def store_robot_action(self, robot_action_dict): + # TODO ISPyB is not ready for now. This prevents from error 500 from the server. + pass + def prepare_collect_for_lims(self, mx_collect_dict): # Attention! directory passed by reference. modified in place diff --git a/mxcubecore/HardwareObjects/DESY/P11NanoDiff.py b/mxcubecore/HardwareObjects/DESY/P11NanoDiff.py index e327c48faa..a23f782fea 100644 --- a/mxcubecore/HardwareObjects/DESY/P11NanoDiff.py +++ b/mxcubecore/HardwareObjects/DESY/P11NanoDiff.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with MXCuBE. If not, see . -__copyright__ = """ Copyright © 2010 - 2023 by MXCuBE Collaboration """ +__copyright__ = """ Copyright © 2010 - 2024 by MXCuBE Collaboration """ __license__ = "LGPLv3+" import sys @@ -118,20 +118,20 @@ def init(self): # using sample_centring module self.centring_sampx = sample_centring.CentringMotor( - self.motor_hwobj_dict["sampx"], units="microns", + self.motor_hwobj_dict["sampx"], units="microns" ) self.centring_sampy = sample_centring.CentringMotor( - self.motor_hwobj_dict["sampy"], units="microns", + self.motor_hwobj_dict["sampy"], units="microns" ) self.centring_phi = sample_centring.CentringMotor( - self.motor_hwobj_dict["phi"], direction=-1, + self.motor_hwobj_dict["phi"], direction=-1 ) self.centring_phiz = sample_centring.CentringMotor( self.motor_hwobj_dict["phiz"], direction=1, units="microns" ) self.centring_phiy = sample_centring.CentringMotor( - self.motor_hwobj_dict["phiy"], direction=1, units="microns", + self.motor_hwobj_dict["phiy"], direction=1, units="microns" ) self.detcover_hwobj = self.get_object_by_role("detector-cover") @@ -154,7 +154,8 @@ def init(self): self.update_phase() self.update_zoom_calibration() - self.beam_position = self.update_beam_position() + # self.beam_position = self.update_beam_position() + self.update_beam_position() def update_beam_position(self): zoom_hwobj = self.motor_hwobj_dict["zoom"] @@ -737,9 +738,9 @@ def detector_cover_close(self, wait=True): def wait_detcover(self, state, timeout=60): start_time = time.time() while time.time() - start_time > timeout: - if state == "open" and self.detcover_hwobj.is_open(): + if state == "open" and self.detcover_hwobj.is_open: break - elif state == "close" and self.detcover_hwobj.is_closed(): + elif state == "close" and self.detcover_hwobj.is_closed: break gevent.sleep(0.5) @@ -802,8 +803,8 @@ def update_phase(self, value=None): omega_pos = self.get_omega_position() - cover_open = self.detcover_hwobj.is_open() - cover_closed = self.detcover_hwobj.is_closed() + cover_open = self.detcover_hwobj.is_open + cover_closed = self.detcover_hwobj.is_closed blight_in = self.backlight_hwobj.is_in() blight_out = self.backlight_hwobj.is_out() collim = self.collimator_hwobj.get_position() diff --git a/mxcubecore/HardwareObjects/DESY/P11Pinhole.py b/mxcubecore/HardwareObjects/DESY/P11Pinhole.py index dd000b31d5..8710970efa 100644 --- a/mxcubecore/HardwareObjects/DESY/P11Pinhole.py +++ b/mxcubecore/HardwareObjects/DESY/P11Pinhole.py @@ -33,7 +33,7 @@ class P11Pinhole(MotorsNPosition): def __init__(self, name): - super(P11Pinhole, self).__init__(name) + super().__init__(name) self._config_file = None @@ -44,7 +44,7 @@ def init(self): self._config_file = self.get_property("config_file") - super(P11Pinhole, self).init() + super().init() def load_positions(self): @@ -57,7 +57,7 @@ def load_positions(self): names = config["Pinholes"]["pinholesizelist"].split(",") names[0] = "Down" - units = ["micron",] * len(names) + units = ["micron"] * len(names) units[0] = "" posnames = copy.copy(names) diff --git a/mxcubecore/HardwareObjects/DESY/P11SampleChanger.py b/mxcubecore/HardwareObjects/DESY/P11SampleChanger.py index 2ec48d57ab..67cc3d47ad 100644 --- a/mxcubecore/HardwareObjects/DESY/P11SampleChanger.py +++ b/mxcubecore/HardwareObjects/DESY/P11SampleChanger.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with MXCuBE. If not, see . -__copyright__ = """ Copyright © 2010 - 2023 by MXCuBE Collaboration """ +__copyright__ = """ Copyright © 2010 - 2024 by MXCuBE Collaboration """ __license__ = "LGPLv3+" @@ -41,7 +41,7 @@ class P11SampleChanger(SampleChanger): NO_OF_SAMPLES_IN_BASKET = 16 def __init__(self, *args, **kwargs): - super(P11SampleChanger, self).__init__(self.__TYPE__, False, *args, **kwargs) + super().__init__(self.__TYPE__, False, *args, **kwargs) def init(self): self._selected_sample = -1 @@ -205,6 +205,7 @@ def load(self, sample=None, wait=True): "Sample changer: Sample loaded (total time: %s)" % (time.time() - self._start_load) ) + self.emit("progressStop", ()) return self.get_loaded_sample() @@ -476,6 +477,7 @@ def _update_selection(self): # find sample for s in self.get_sample_list(): + # print(f"Sample coords = {s.get_coords()}") if s.get_coords() == (basket, sample): self.log.debug(" - sample found") self._set_loaded_sample(s) diff --git a/mxcubecore/HardwareObjects/DESY/P11Session.py b/mxcubecore/HardwareObjects/DESY/P11Session.py index fc959c288a..466f875c51 100644 --- a/mxcubecore/HardwareObjects/DESY/P11Session.py +++ b/mxcubecore/HardwareObjects/DESY/P11Session.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with MXCuBE. If not, see . -__copyright__ = """ Copyright © 2010 - 2023 by MXCuBE Collaboration """ +__copyright__ = """ Copyright © 2010 - 2024 by MXCuBE Collaboration """ __license__ = "LGPLv3+" import os @@ -41,11 +41,11 @@ class P11Session(Session): default_archive_folder = "raw" def __init__(self, *args): - super(P11Session, self).__init__(*args) + super().__init__(*args) def init(self): - super(P11Session, self).init() + super().init() self.settings_file = self.get_property("p11_settings_file") self.operation_mode = self.get_property("mode") @@ -85,8 +85,19 @@ def info_set_defaults(self): self.beamtime_info["rootPath"] = PATH_FALLBACK def is_beamtime_open(self): - return True - # return self.is_writable_dir( os.path.join(PATH_BEAMTIME, self.raw_data_folder_name) ) + self.log.debug("=========== CHECKING IF BEAMTIME ID IS OPEN... ============") + if self.is_writable_dir(os.path.join(PATH_BEAMTIME, self.raw_data_folder_name)): + self.log.debug( + "=========== BEAMTIME IS OPEN (/gpfs/current exists) ============" + ) + else: + self.log.debug( + "=========== NO BEMTIME ID IS OPEN (check /gpfs/current) ============" + ) + + return self.is_writable_dir( + os.path.join(PATH_BEAMTIME, self.raw_data_folder_name) + ) def is_commissioning_open(self): return self.is_writable_dir( @@ -111,6 +122,9 @@ def get_current_proposal_number(self): info = self.get_beamtime_info() return info["proposalId"] + def get_beamtime_info(self): + return self.beamtime_info + def read_beamtime_info(self): self.log.debug("=========== READING BEAMTIME INFO ============") if os.path.exists(PATH_BEAMTIME): diff --git a/mxcubecore/HardwareObjects/DESY/P11Shutter.py b/mxcubecore/HardwareObjects/DESY/P11Shutter.py index 3a147822dc..a09382f891 100644 --- a/mxcubecore/HardwareObjects/DESY/P11Shutter.py +++ b/mxcubecore/HardwareObjects/DESY/P11Shutter.py @@ -22,6 +22,7 @@ import gevent import urllib from mxcubecore.HardwareObjects.abstract.AbstractShutter import AbstractShutter +import mxcubecore.HardwareObjects.abstract.AbstractShutter as absshut from mxcubecore.BaseHardwareObjects import HardwareObjectState from enum import Enum, unique @@ -40,17 +41,18 @@ class P11Shutter(AbstractShutter): @unique class BaseValueEnum(Enum): - """Defines only the compulsory values.""" - OPEN = "OPEN" - CLOSED = "CLOSED" - MOVING = "MOVING" - UNKNOWN = "UNKNOWN" - - VALUES=BaseValueEnum + """Defines only the compulsory values.""" + + OPEN = "OPEN" + CLOSED = "CLOSED" + MOVING = "MOVING" + UNKNOWN = "UNKNOWN" + + VALUES = BaseValueEnum def __init__(self, name): - super(AbstractShutter, self).__init__(name) + super().__init__(name) self.simulation = False self.simulated_opened = True @@ -68,6 +70,7 @@ def init(self): # if simulation is set - open and close will be mere software flags self.simulation = self.get_property("simulation") + self._initialise_values() if not self.simulation: url_base = self.get_property("base_url") @@ -85,7 +88,15 @@ def init(self): else: self.simulated_update() - super(AbstractShutter, self).init() + self.update_state(self.STATES.READY) + + super().init() + + def _initialise_values(self): + """Add additional, known in advance states to VALUES""" + values_dict = {item.name: item.value for item in self.VALUES} + values_dict.update({"MOVING": "MOVING"}) + self.VALUES = Enum("ValueEnum", values_dict) def get_value(self): if self.simulation: @@ -105,25 +116,40 @@ def _set_value(self, value): if self.simulation: self.simulated_opened = open_it self.simulated_moving = True - gevent.spawn(self.simul_do) + self.t1 = gevent.spawn(self.simul_do) + self.t1.link(self.do_finish) + self.t1.link_exception(self.do_finish_exc) else: if open_it: self.do_open() else: self.do_close() self.cmd_started = time.time() + self.log.debug(" ### setting value value for shutter done") def do_open(self, timeout=3): + self.log.debug(" OPENING SHUTTER (web request)") result = urllib.request.urlopen(self.url_open, None, timeout).readlines() + self.log.debug(" OPENING SHUTTER (web request) retured") def do_close(self, timeout=3): + self.log.debug(" CLOSING SHUTTER (web request)") result = urllib.request.urlopen(self.url_close, None, timeout).readlines() + self.log.debug(" CLOSING SHUTTER (web request) retured") def simul_do(self): + self.log.debug("### starting simulated shutter move") gevent.sleep(1) self.simulated_moving = False self.log.debug("### updating simulated shutter") self.simulated_update() + self.log.debug("### ending simulated shutter move") + + def do_finish(self, t=None): + self.log.debug("### simulated finished") + + def do_finish_exc(self, exc=None): + self.log.debug("### simulated finished with exception") def update_shutter_state(self, state=None): """Updates shutter state @@ -133,9 +159,13 @@ def update_shutter_state(self, state=None): if state is None: state = self.chan_state.get_value() + self.log.debug(" SHUTTER state changed") + if state[0] == 3: + self.log.debug(" P11SHUTTER IS OPEN") value = self.VALUES.OPEN else: + self.log.debug(" P11SHUTTER IS CLOSED") value = self.VALUES.CLOSED # else: @@ -146,6 +176,7 @@ def update_shutter_state(self, state=None): self.update_value(value) + self.log.debug(" update shutter state done") return value def simulated_update(self): diff --git a/mxcubecore/HardwareObjects/DESY/P11Transmission.py b/mxcubecore/HardwareObjects/DESY/P11Transmission.py index 9f564a441c..9b929072b5 100644 --- a/mxcubecore/HardwareObjects/DESY/P11Transmission.py +++ b/mxcubecore/HardwareObjects/DESY/P11Transmission.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with MXCuBE. If not, see . -__copyright__ = """ Copyright © 2010 - 2023 by MXCuBE Collaboration """ +__copyright__ = """ Copyright © 2010 - 2024 by MXCuBE Collaboration """ __license__ = "LGPLv3+" @@ -35,7 +35,7 @@ class P11Transmission(AbstractTransmission): def __init__(self, name): - super(P11Transmission, self).__init__(name) + super().__init__(name) self.chan_read_value = None self.chan_set_value = None diff --git a/mxcubecore/HardwareObjects/DESY/ValueStateChannel.py b/mxcubecore/HardwareObjects/DESY/ValueStateChannel.py index f94f640843..fdab0a73e5 100644 --- a/mxcubecore/HardwareObjects/DESY/ValueStateChannel.py +++ b/mxcubecore/HardwareObjects/DESY/ValueStateChannel.py @@ -23,7 +23,7 @@ class ValueStateChannel(HardwareObject): def __init__(self, name): - super(ValueStateChannel, self).__init__(name) + super().__init__(name) def init(self):