Skip to content

Commit

Permalink
Yaml config - bliss classes
Browse files Browse the repository at this point in the history
  • Loading branch information
Antonia Beteva committed Jan 6, 2025
1 parent 4a8a23d commit a389953
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 100 deletions.
44 changes: 30 additions & 14 deletions mxcubecore/HardwareObjects/Bliss.py
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
"""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+"


Expand All @@ -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"] = "<no file>"
Expand Down
81 changes: 44 additions & 37 deletions mxcubecore/HardwareObjects/BlissEnergy.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,26 @@
# along with MXCuBE. If not, see <http://www.gnu.org/licenses/>.
"""
Energy and Wavelength with bliss.
Example xml file:
- for tunable wavelength beamline:
<object class="Energy">
<object href="/energy" role="energy_motor"/>
<object href="/bliss" role="bliss"/>
</object>
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:
<object class="Energy">
<read_only>True</read_only>
<energy>12.8123</energy>
or
<object href="/energy" role="energy_motor"/>
The energy should have methods get_value and get_state
</object>
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
Expand All @@ -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+"


Expand All @@ -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):
Expand All @@ -86,22 +92,22 @@ 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
Args:
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.
Expand Down Expand Up @@ -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"""
Expand Down
16 changes: 9 additions & 7 deletions mxcubecore/HardwareObjects/BlissMotor.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
# You should have received a copy of the GNU General Lesser Public License
# along with MXCuBE. If not, see <http://www.gnu.org/licenses/>.
"""
Example xml file:
<object class="BlissMotor">
<username>Detector Distance</username>
<actuator_name>dtox</actuator_name>
<tolerance>1e-2</tolerance>
</object>
Example yml configuration:
.. code-block:: yaml
class: BlissMotor.BlissMotor
configuration:
actuator_name: dtox
username: Detector Distance
"""

import enum
Expand All @@ -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+"


Expand Down
65 changes: 35 additions & 30 deletions mxcubecore/HardwareObjects/BlissNState.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,20 @@

"""
bliss implementation of AbstartNState
Example xml file:
<object class="BlissNState">
<username>Detector Cover</username>
<actuator_name>detcover</>
<object href="/bliss" role="controller"/>
<values>{"IN": "IN", "OUT": "OUT"}</values>
</object>
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

Expand All @@ -36,7 +43,7 @@
BaseValueEnum,
)

__copyright__ = """ Copyright © 2020 by the MXCuBE collaboration """
__copyright__ = """ Copyright © by the MXCuBE collaboration """
__license__ = "LGPLv3+"


Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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(
Expand Down
Loading

0 comments on commit a389953

Please sign in to comment.