Source code for mxcubecore.HardwareObjects.DESY.P11Collimator

# encoding: utf-8
#
#  Project name: 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 <http://www.gnu.org/licenses/>.

__copyright__ = """Copyright The MXCuBE Collaboration"""
__license__ = "LGPLv3+"

import ast
from collections import OrderedDict

from mxcubecore.BaseHardwareObjects import HardwareObjectState
from mxcubecore.HardwareObjects.NState import NState


[docs]class P11Collimator(NState): """Collimator hardware object class"""
[docs] def init(self): """Initialize the collimator with its motors and positions.""" super().init() # Retrieve motors using their roles self.y_motor = self.get_object_by_role("collimatory") # Y-axis motor self.z_motor = self.get_object_by_role("collimatorz") # Z-axis motor self.log.info(f"Collimator Y Motor initialized: {self.y_motor}") self.log.info(f"Collimator Z Motor initialized: {self.z_motor}") # Load positions from XML configuration self.load_positions() # Load deltas for each motor self.load_deltas() # Set _positions for UI access self._positions = OrderedDict() self._positions = self.positions
[docs] def load_positions(self): """Load predefined positions from the XML configuration.""" self.log.info("Loading collimator positions from config") positions_str = self.get_property("values") # Convert the string to a dictionary using ast.literal_eval try: self.positions = ast.literal_eval(positions_str) if isinstance(self.positions, dict): self.log.info(f"Available collimator positions: {self.positions}") else: raise ValueError("Positions data is not a dictionary") except (SyntaxError, ValueError) as e: self.log.error(f"Error parsing collimator positions: {e}") raise ValueError( "Invalid collimator positions format in the configuration." )
[docs] def load_deltas(self): """Load individual motor deltas from the XML configuration explicitly.""" self.log.info("Loading deltas from config") # Fetch individual deltas for each motor delta_y = self.get_property("delta_collimatory") delta_z = self.get_property("delta_collimatorz") # If a delta is not specified, fallback to a default delta value self.deltas = { "collimatory": ( float(delta_y) if delta_y is not None else self.default_delta ), "collimatorz": ( float(delta_z) if delta_z is not None else self.default_delta ), } # Log the deltas for each motor for motorname, delta in self.deltas.items(): self.log.info(f"Delta for {motorname}: {delta}")
[docs] def set_value(self, value): """Set the collimator to the specified position.""" if value not in self.positions: raise ValueError(f"Invalid state value: {value}") y_position = self.positions[value]["collimatory"] z_position = self.positions[value]["collimatorz"] # Move the motors self.y_motor._set_value(y_position) self.z_motor._set_value(z_position) self.log.info(f"Setting collimator to position: {value}")
[docs] def get_position_list(self): """Return the list of available collimator positions.""" return list(self.positions.keys())
[docs] def get_value(self): """Get the current collimator position based on the motor positions.""" current_y = self.y_motor.get_value() current_z = self.z_motor.get_value() for position_name, position in self.positions.items(): if self.is_within_deltas( position.get("collimatory"), current_y, "collimatory" ) and self.is_within_deltas( position.get("collimatorz"), current_z, "collimatorz" ): return position_name # Return the matching position name
[docs] def is_within_deltas(self, target_value, current_value, motor_name): """Check if the current motor position is within the delta tolerance for that specific motor.""" delta = self.deltas.get(motor_name) if target_value is None or delta is None: return False return abs(current_value - target_value) <= delta
[docs] def is_moving(self): """Return True if any motor is moving.""" return self.y_motor.is_moving() or self.z_motor.is_moving()
[docs] def get_state(self): """Determine the overall state of the collimator motor system.""" if self.is_moving(): return HardwareObjectState.BUSY else: return HardwareObjectState.READY