From a38995372ec9655e2e6bffcec8315662b5c2ab6e Mon Sep 17 00:00:00 2001 From: Antonia Beteva Date: Mon, 6 Jan 2025 17:54:00 +0100 Subject: [PATCH] Yaml config - bliss classes --- mxcubecore/HardwareObjects/Bliss.py | 44 ++++++++---- mxcubecore/HardwareObjects/BlissEnergy.py | 81 ++++++++++++---------- mxcubecore/HardwareObjects/BlissMotor.py | 16 +++-- mxcubecore/HardwareObjects/BlissNState.py | 65 +++++++++-------- mxcubecore/HardwareObjects/BlissShutter.py | 27 ++++---- 5 files changed, 133 insertions(+), 100 deletions(-) diff --git a/mxcubecore/HardwareObjects/Bliss.py b/mxcubecore/HardwareObjects/Bliss.py index 143217abcf..c4fbe80d82 100644 --- a/mxcubecore/HardwareObjects/Bliss.py +++ b/mxcubecore/HardwareObjects/Bliss.py @@ -1,20 +1,38 @@ +# 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 +# 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 General Lesser Public License +# along with MXCuBE. If not, see . """Bliss session and tools for sending the scan data for plotting. Emits new_plot, plot_data and plot_end. +Example yaml file: +.. code-block:: yaml + + class: Bliss.Bliss + configuration: + session: mxcubebliss """ import itertools -import gevent import numpy from bliss.config import static -from bliss.data.node import ( - DataNode, - get_or_create_node, -) from mxcubecore.BaseHardwareObjects import HardwareObject -__copyright__ = """ Copyright © 2019 by the MXCuBE collaboration """ +__copyright__ = """ Copyright © by the MXCuBE collaboration """ __license__ = "LGPLv3+" @@ -27,26 +45,24 @@ def all_equal(iterable): class Bliss(HardwareObject): """Bliss class""" - def __init__(self, *args): - HardwareObject.__init__(self, *args) + def __init__(self, name): + super().__init__(name) self.__scan_data = {} + self.session_name = None - def init(self, *args): - """Initialis the bliss session""" + def init(self): + """Initialise the bliss session""" cfg = static.get_config() session = cfg.get(self.get_property("session")) - session.setup(self.__dict__, verbose=True) - self.__scan_data = dict() - def __on_scan_new(self, scan_info): """New scan. Emit new_plot. Args: scan_info(dict): Contains SCAN_INFO dictionary from bliss """ scan_id = scan_info["scan_nb"] - self.__scan_data[scan_id] = list() + self.__scan_data[scan_id] = [] if not scan_info["save"]: scan_info["root_path"] = "" diff --git a/mxcubecore/HardwareObjects/BlissEnergy.py b/mxcubecore/HardwareObjects/BlissEnergy.py index 30ac2e609d..da90aec0e3 100644 --- a/mxcubecore/HardwareObjects/BlissEnergy.py +++ b/mxcubecore/HardwareObjects/BlissEnergy.py @@ -17,23 +17,26 @@ # along with MXCuBE. If not, see . """ Energy and Wavelength with bliss. -Example xml file: - - for tunable wavelength beamline: - - - - -The energy should have methods get_value, get_limits, get_state and move. -If used, the controller should have method moveEnergy. - - - for fixed wavelength beamline: - - True - 12.8123 - or - -The energy should have methods get_value and get_state - +Example yaml file: +- for tunable wavelength beamline: +.. code-block:: yaml + + class: BlissEnergy.BlissEnergy + configuration: + username: Energy + objects: + controller: bliss.yml + energy_motor: energy_motor.yml + +- for fixed wavelength beamline: +.. code-block:: yaml + + class: BlissEnergy.BlissEnergy + configuration: + username: Energy + read_only: True + default_value: 12.8123 + """ import logging import math @@ -43,7 +46,7 @@ from mxcubecore.BaseHardwareObjects import HardwareObjectState from mxcubecore.HardwareObjects.abstract.AbstractEnergy import AbstractEnergy -__copyright__ = """ Copyright © 2019 by the MXCuBE collaboration """ +__copyright__ = """ Copyright © by the MXCuBE collaboration """ __license__ = "LGPLv3+" @@ -52,32 +55,35 @@ class BlissEnergy(AbstractEnergy): def __init__(self, name): super().__init__(name) - self._energy_motor = None - self._bliss_session = None + self.energy_motor = None + self.controller = None self._cmd_execution = None def init(self): """Initialisation""" super().init() - self._energy_motor = self.get_object_by_role("energy_motor") - self._bliss_session = self.get_object_by_role("bliss") + self.update_state(HardwareObjectState.READY) - if self._energy_motor: - self.update_state(self._energy_motor.get_state()) - self._energy_motor.connect("valueChanged", self.update_value) - self._energy_motor.connect("stateChanged", self.update_state) + if self.energy_motor: + self.update_state(self.energy_motor.get_state()) + self.energy_motor.connect("valueChanged", self.update_value) + self.energy_motor.connect("stateChanged", self.update_state) - if self.read_only and not self._energy_motor: - self._nominal_value = float(self.get_property("energy", 0)) + if self.read_only and not self.energy_motor: + #self._nominal_value = float(self.get_property("energy", 0)) + try: + self._nominal_value = float(self.default_value) + except TypeError as err: + raise RuntimeError("Energy not defined") from err def get_value(self): """Read the energy. Returns: (float): Energy [keV] """ - if self._energy_motor: - self._nominal_value = self._energy_motor.get_value() + if self.energy_motor: + self._nominal_value = self.energy_motor.get_value() return self._nominal_value def get_limits(self): @@ -86,12 +92,12 @@ def get_limits(self): (tuple): two floats tuple (low limit, high limit) [keV]. """ if not self.read_only: - self._nominal_limits = self._energy_motor.get_limits() + self._nominal_limits = self.energy_motor.get_limits() return self._nominal_limits def stop(self): """Stop the energy motor movement""" - self._energy_motor.stop() + self.energy_motor.stop() def _set_value(self, value): """Execute the sequence to move to an energy @@ -99,9 +105,9 @@ def _set_value(self, value): value (float): target energy """ try: - self._bliss_session.change_energy(value) - except RuntimeError: - self._energy_motor.set_value(value) + self.controller.change_energy(value) + except (AttributeError, RuntimeError): + self.energy_motor.set_value(value) def set_value(self, value, timeout=0): """Move energy to absolute position. Wait the move to finish. @@ -136,9 +142,10 @@ def set_value(self, value, timeout=0): else: self._cmd_execution = spawn(self._set_value(value)) else: - self._energy_motor.set_value(value, timeout=timeout) + self.energy_motor.set_value(value, timeout=timeout) else: - raise ValueError("Invalid value %s" % str(value)) + msg = f"Invalid value {value}" + raise ValueError(msg) def abort(self): """Abort the procedure""" diff --git a/mxcubecore/HardwareObjects/BlissMotor.py b/mxcubecore/HardwareObjects/BlissMotor.py index 18aa10f781..9c7d682642 100644 --- a/mxcubecore/HardwareObjects/BlissMotor.py +++ b/mxcubecore/HardwareObjects/BlissMotor.py @@ -18,12 +18,14 @@ # You should have received a copy of the GNU General Lesser Public License # along with MXCuBE. If not, see . """ -Example xml file: - - Detector Distance - dtox - 1e-2 - +Example yml configuration: + +.. code-block:: yaml + + class: BlissMotor.BlissMotor + configuration: + actuator_name: dtox + username: Detector Distance """ import enum @@ -33,7 +35,7 @@ from mxcubecore.BaseHardwareObjects import HardwareObjectState from mxcubecore.HardwareObjects.abstract.AbstractMotor import AbstractMotor -__copyright__ = """ Copyright © 2019 by the MXCuBE collaboration """ +__copyright__ = """ Copyright © by the MXCuBE collaboration """ __license__ = "LGPLv3+" diff --git a/mxcubecore/HardwareObjects/BlissNState.py b/mxcubecore/HardwareObjects/BlissNState.py index d3bd09af6d..01cfa89709 100644 --- a/mxcubecore/HardwareObjects/BlissNState.py +++ b/mxcubecore/HardwareObjects/BlissNState.py @@ -20,13 +20,20 @@ """ bliss implementation of AbstartNState -Example xml file: - - Detector Cover - detcover - - {"IN": "IN", "OUT": "OUT"} - + +Example yaml file: + +.. code-block:: yaml + + class: BlissNState.BlissNState + configuration: + actuator_name: detcover + prefix: detcov # optional + type: actuator # actuaror or motor, default value actuator + username: Detector Cover + values: {"IN": "IN", "OUT": "OUT"} # optional + objects: + controller: bliss.yml """ from enum import Enum @@ -36,7 +43,7 @@ BaseValueEnum, ) -__copyright__ = """ Copyright © 2020 by the MXCuBE collaboration """ +__copyright__ = """ Copyright © by the MXCuBE collaboration """ __license__ = "LGPLv3+" @@ -48,35 +55,32 @@ class BlissNState(AbstractNState): def __init__(self, name): super().__init__(name) self._bliss_obj = None - self.device_type = None + self.type = None self.__saved_state = None - self._prefix = None + self.prefix = None def init(self): """Initialise the device""" super().init() - self._prefix = self.get_property("prefix") - self._bliss_obj = getattr( - self.get_object_by_role("controller"), self.actuator_name - ) + self._bliss_obj = getattr(self.controller, self.actuator_name) - self.device_type = self.get_property("type", "actuator") + self.type = self.get_property("type", "actuator") if "MultiplePositions" in self._bliss_obj.__class__.__name__: - self.device_type = "motor" + self.type = "motor" self.initialise_values() - if self.device_type == "actuator": + if self.type == "actuator": self.connect(self._bliss_obj, "state", self.update_value) self.connect(self._bliss_obj, "state", self._update_state) self.__saved_state = self.get_value() - elif self.device_type == "motor": + elif self.type == "motor": self.connect(self._bliss_obj, "position", self.update_value) self.connect(self._bliss_obj, "state", self._update_state_motor) self.update_state() - # NB: Bliss calls the update handler with the state so its needed in the + # NB: Bliss calls the update handler with the state so it is neded in the # method definition def _update_state(self, state=None): self.update_state(self.STATES.READY) @@ -86,11 +90,12 @@ def get_value(self): Returns: (Enum): Enum member, corresponding to the value or UNKNOWN. """ - if self.device_type == "motor": + _val = "UNKNOWN" + if self.type == "motor": _val = self._bliss_obj.position - elif self.device_type == "actuator": - if self._prefix: - _attr = self._prefix + "_is_in" + elif self.type == "actuator": + if self.prefix: + _attr = self.prefix + "_is_in" _cmd = getattr(self._bliss_obj, _attr) if isinstance(_cmd, bool): _val = _cmd @@ -115,12 +120,12 @@ def _set_value(self, value): svalue = value.value else: self.__saved_state = value.upper() - # are we sure we never get to need svalue below without setting it first? - if self.device_type == "motor": + + if self.type == "motor": self._bliss_obj.move(svalue, wait=False) - elif self.device_type == "actuator": - if self._prefix: - _attr = self._prefix + "_" + value.name.lower() + elif self.type == "actuator": + if self.prefix: + _attr = self.prefix + "_" + value.name.lower() else: _attr = "set_" + svalue.lower() _cmd = getattr(self._bliss_obj, _attr) @@ -160,9 +165,9 @@ def initialise_values(self): Returns: (Enum): "ValueEnum" with predefined values. """ - if self.device_type == "actuator": + if self.type == "actuator": super().initialise_values() - if self.device_type == "motor": + if self.type == "motor": try: values = {val.upper(): val for val in self._bliss_obj.positions_list} self.VALUES = Enum( diff --git a/mxcubecore/HardwareObjects/BlissShutter.py b/mxcubecore/HardwareObjects/BlissShutter.py index 0b8d8c31e6..e98fa5c8c0 100644 --- a/mxcubecore/HardwareObjects/BlissShutter.py +++ b/mxcubecore/HardwareObjects/BlissShutter.py @@ -22,13 +22,17 @@ Implements _set_value, get_value methods Bliss states are: UNKNOWN, OPEN, CLOSED, FAULT "MOVING", "DISABLE", "STANDBY", "RUNNING" -Example xml file: - - Safety Shutter - safshut - tango - - +Example yml configuration: + +.. code-block:: yaml + + class: BlissShutter.BlissShutter + configuration: + actuator_name: safshut + type: tango + username: Safety shutter + objects: + controller: bliss.yaml """ from enum import ( Enum, @@ -40,7 +44,7 @@ from mxcubecore.BaseHardwareObjects import HardwareObjectState from mxcubecore.HardwareObjects.abstract.AbstractShutter import AbstractShutter -__copyright__ = """ Copyright © 2020 by the MXCuBE collaboration """ +__copyright__ = """ Copyright © by the MXCuBE collaboration """ __license__ = "LGPLv3+" @@ -63,16 +67,15 @@ class BlissShutter(AbstractShutter): SPECIFIC_STATES = BlissShutterStates def __init__(self, name): - AbstractShutter.__init__(self, name) + super().__init__(name) self._bliss_obj = None self.shutter_type = None self.opening_mode = None def init(self): """Initilise the predefined values""" - AbstractShutter.init(self) - _name = self.get_property("name") - self._bliss_obj = getattr(self.get_object_by_role("controller"), _name) + super().init() + self._bliss_obj = getattr(self.controller, self.actuator_name) # for now we only treat tango type shutter self.shutter_type = self.get_property("type", "tango") try: