Source code for contrast.motors.SmaractMotor

"""
Provides a ``Motor`` subclass for Smaract positioners. All numbers in
micrometers.

Controls these via a MAX IV Tango device,
https://gitlab.maxiv.lu.se/kits-maxiv/dev-maxiv-mcs
"""

try:
    import tango
except ImportError:
    pass
from . import Motor
import time
import math


[docs] class SmaractLinearMotor(Motor): """ Single Smaract motor axis. """ def __init__(self, device, axis, velocity=None, frequency=None, **kwargs): """ :param device: Path to the MCS Tango device :type device: str :param axis: Axis number on the controller :type axis: int :param velocity: Initialize velocity, defaults to None :type velocity: float :param ``**kwargs``: Passed on to the ``Motor`` base class """ super().__init__(**kwargs) self.proxy = tango.DeviceProxy(device) self.proxy.set_source(tango.DevSource.DEV) self.axis = int(axis) if velocity is not None: attr = 'velocity_%d' % self.axis self.proxy.write_attribute(attr, velocity * 1e3) if frequency is not None: self.proxy.arbitraryCommand("SCLF%u,%u" % (self.axis, frequency)) @property def dial_position(self): attr = 'position_%d' % self.axis return self.proxy.read_attribute(attr).value * 1e-3 @dial_position.setter def dial_position(self, pos): attr = 'position_%d' % self.axis self.proxy.write_attribute(attr, pos * 1e3) def busy(self): attr = 'state_%d' % self.axis return not (self.proxy.read_attribute(attr).value == tango.DevState.ON) def home(self): print('\nhoming %s...' % self.name) self.proxy.arbitraryCommand("FRM%u,2,60000,1" % self.axis) while self.busy(): time.sleep(.1) print('homing done') def frequency(self, freq): # set the maximum closed loop frequency of the positioner self.proxy.arbitraryCommand(f"SCLF{self.axis:d},{int(freq):d}") def stop(self): self.proxy.stopOne(self.axis) # safety first def health_check(self): # run anything that is in the parent classes super().health_check() # check if the encoder is in energy saving mode (not prefered) or always on (prefered) state_power_mode = self.proxy.read_attribute('power_mode').value if state_power_mode != 'enabled': print(f'\033[91m[!]\033[0m {self.name}: The motor is not in always on mode. Power Saving mode can result in lost positions.') # check if the stage is homed reference_position_known = self.proxy.read_attribute(f'physical_position_known_{self.axis}').value if reference_position_known != True: print(f'\033[91m[!]\033[0m {self.name}: The motor is not homed. SmarAct stages do not have absolute encoders.')
class SmaractRotationMotor(SmaractLinearMotor): @property def dial_position(self): attr = 'angle_%d' % self.axis result = self.proxy.read_attribute(attr).value result = result.split(',') pos = int(result[0]) rev = int(result[1]) pos = pos * 1e-6 + rev * 360 return pos @dial_position.setter def dial_position(self, pos): angle = int(1e6 * (pos % 360)) rev = int(pos // 360) attr = 'angle_%d' % self.axis val = '%d,%d' % (angle, rev) self.proxy.write_attribute(attr, val)
[docs] class SmaractLinearMotor_MCS2(Motor): """ Single Smaract MCS2 motor axis. """ def __init__(self, device, axis, velocity=None, acceleration=None, hold_time=None, **kwargs): """ :param device: Path to the MCS2 Tango device :type device: str :param axis: Axis number on the controller :type axis: int :param velocity: Initialize velocity, defaults to None :type velocity: float :param acceleration: Initialize acceleration, defaults to None :type acceleration: float :param hold_time: Initialize hold_time, defaults to None :type hold_time: float :param ``**kwargs``: Passed on to the ``Motor`` base class """ super().__init__(**kwargs) self.proxy = tango.DeviceProxy(device) self.proxy.set_source(tango.DevSource.DEV) self.axis = int(axis) if velocity is not None: attr = 'velocity_%d' % self.axis self.proxy.write_attribute(attr, velocity) if acceleration is not None: attr = 'acceleration_%d' % self.axis self.proxy.write_attribute(attr, acceleration) if hold_time is not None: attr = 'hold_time_%d' % self.axis self.proxy.write_attribute(attr, hold_time) @property def dial_position(self): attr = 'position_%d' % self.axis val = self.proxy.read_attribute(attr).value pos = val * 1e3 return pos @dial_position.setter def dial_position(self, pos): attr = 'position_%d' % self.axis val = pos * 1e-3 self.proxy.write_attribute(attr, val) def stop(self): self.proxy.stop(self.axis) def busy(self): attr = 'state_%d' % self.axis return not (self.proxy.read_attribute(attr).value == tango.DevState.ON) def reference(self): self.proxy.reference(self.axis) def health_check(self): # run anything that is in the parent classes super().health_check() # check if the encoder is in energy saving mode (not prefered) or always on (prefered) status = self.proxy.read_attribute(f'status_{self.axis}').value if status.find('IS_REFERENCED : False') > -1: print(f'\033[91m[!]\033[0m {self.name}: The motor is not homed. SmarAct stages do not have absolute encoders.')
class SmaractRotationMotor_MCS2(SmaractLinearMotor_MCS2): @property def dial_position(self): attr = 'position_%d' % self.axis val = self.proxy.read_attribute(attr).value pos = val * 360/600 return pos @dial_position.setter def dial_position(self, pos): attr = 'position_%d' % self.axis val = pos * 600/360 self.proxy.write_attribute(attr, val)