diff --git a/README.rst b/README.rst index 882c569..a584324 100644 --- a/README.rst +++ b/README.rst @@ -42,6 +42,7 @@ Actuators * **PRM1Z8_pylablib**: DC servo motorized 360° rotation mount (Thorlabs PRM1Z8) using the pylablib control module. The Thorlabs APT software should be installed: https://www.thorlabs.com/newgrouppage9.cfm?objectgroup_id=9019. * **BrushlessDCMotor**: Kinesis control of DC Brushless Motor (tested with the BBD201 controller) * **Kinesis_KPZ101**: Piezo Electric Stage Kinesis series (KPZ101) +* **Kinesis_KDC101**: DC Servo Motor Kinesis series (KDC101) Viewer0D diff --git a/src/pymodaq_plugins_thorlabs/daq_move_plugins/daq_move_KDC101.py b/src/pymodaq_plugins_thorlabs/daq_move_plugins/daq_move_KDC101.py new file mode 100644 index 0000000..ceea14d --- /dev/null +++ b/src/pymodaq_plugins_thorlabs/daq_move_plugins/daq_move_KDC101.py @@ -0,0 +1,130 @@ +from typing import Union, List, Dict +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, comon_parameters_fun, main, DataActuatorType,\ + DataActuator +from pymodaq.utils.daq_utils import ThreadCommand +from pymodaq.utils.parameter import Parameter +from pymodaq_plugins_thorlabs.hardware.kinesis import serialnumbers_dcServo, DCservo as KDC101 + + +class DAQ_Move_KDC101(DAQ_Move_base): + """ Instrument plugin class for an actuator. + + This object inherits all functionalities to communicate with PyMoDAQ’s DAQ_Move module through inheritance via + DAQ_Move_base. It makes a bridge between the DAQ_Move module and the Python wrapper of a particular instrument. + + Attributes: + ----------- + controller: object + The particular object that allow the communication with the hardware, in general a python wrapper around the + hardware library. + + """ + is_multiaxes = False + _axis_names: Union[List[str], Dict[str, int]] = {'1': 1} + _controller_units: Union[str, List[str]] = KDC101.default_units + _epsilon: Union[float, List[float]] = 0.2e-3 + data_actuator_type = DataActuatorType.DataActuator + + params = [ + {'title': 'Serial Number:', 'name': 'serial_number', 'type': 'list', + 'limits': serialnumbers_dcServo, 'value': serialnumbers_dcServo[0]}, + ] + comon_parameters_fun(is_multiaxes, axes_names=_axis_names, epsilon=_epsilon) + + def ini_attributes(self): + self.controller: KDC101 = None + + + def get_actuator_value(self): + """Get the current value from the hardware with scaling conversion. + + Returns + ------- + float: The position obtained after scaling conversion. + """ + + pos = DataActuator(data=self.controller.get_position()) + pos = self.get_position_with_scaling(pos) + return pos + + def close(self): + """Terminate the communication protocol""" + self.controller.close() + + def commit_settings(self, param: Parameter): + """Apply the consequences of a change of value in the detector settings + + Parameters + ---------- + param: Parameter + A given parameter (within detector_settings) whose value has been changed by the user + """ + if param.name() == 'units': + self.controller.set_units(self.settings.child(('units')).value()) + else: + pass + + def ini_stage(self, controller=None): + """Actuator communication initialization + + Parameters + ---------- + controller: (object) + custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case) + + Returns + ------- + info: str + initialized: bool + False if initialization failed otherwise True + """ + self.ini_stage_init(slave_controller=controller) + + if self.is_master: + self.controller = KDC101() + self.controller.connect(self.settings['serial_number']) + + info = "KDC101 DCServo initialized" + initialized = True + return info, initialized + + def move_abs(self, value: DataActuator): + """ Move the actuator to the absolute target defined by value + + Parameters + ---------- + value: (float) value of the absolute target positioning + """ + + value = self.check_bound(value) + self.target_value = value + value = self.set_position_with_scaling(value) + self.controller.move_abs(value.value(), 60000) + + def move_rel(self, value: DataActuator): + """ Move the actuator to the relative target actuator value defined by value + + Parameters + ---------- + value: (float) value of the relative target positioning + """ + value = self.check_bound(self.current_position + value) - self.current_position + self.target_value = value + self.current_position + value = self.set_position_relative_with_scaling(value) + + + self.controller.move_rel(value.value(), 60000) + + def move_home(self): + """Call the reference method of the controller""" + + self.controller.home(60000) + + def stop_motion(self): + """Stop the actuator and emits move_done signal""" + + self.controller.stop() + self.emit_status(ThreadCommand('Update_Status', ['KDC101 DCServo stopped'])) + + +if __name__ == '__main__': + main(__file__) diff --git a/src/pymodaq_plugins_thorlabs/hardware/kinesis.py b/src/pymodaq_plugins_thorlabs/hardware/kinesis.py index 9cdd755..4b41405 100644 --- a/src/pymodaq_plugins_thorlabs/hardware/kinesis.py +++ b/src/pymodaq_plugins_thorlabs/hardware/kinesis.py @@ -1,6 +1,9 @@ import clr import sys from typing import Dict +import time +import logging +logger = logging.getLogger(__name__) from System import Decimal from System import Action @@ -19,6 +22,7 @@ clr.AddReference("Thorlabs.MotionControl.FilterFlipperCLI") clr.AddReference("Thorlabs.MotionControl.Benchtop.BrushlessMotorCLI") clr.AddReference("Thorlabs.MotionControl.KCube.PiezoCLI") +clr.AddReference("Thorlabs.MotionControl.KCube.DCServoCLI") import Thorlabs.MotionControl.FilterFlipperCLI as FilterFlipper import Thorlabs.MotionControl.IntegratedStepperMotorsCLI as Integrated @@ -26,6 +30,7 @@ import Thorlabs.MotionControl.GenericMotorCLI as Generic import Thorlabs.MotionControl.Benchtop.BrushlessMotorCLI as BrushlessMotorCLI import Thorlabs.MotionControl.KCube.PiezoCLI as KCubePiezo +import Thorlabs.MotionControl.KCube.DCServoCLI as KCube Device.DeviceManagerCLI.BuildDeviceList() serialnumbers_integrated_stepper = [str(ser) for ser in @@ -35,6 +40,7 @@ serialnumbers_brushless = [str(ser) for ser in Device.DeviceManagerCLI.GetDeviceList(BrushlessMotorCLI.BenchtopBrushlessMotor.DevicePrefix)] serialnumbers_piezo = [str(ser) for ser in Device.DeviceManagerCLI.GetDeviceList(KCubePiezo.KCubePiezo.DevicePrefix)] +serialnumbers_dcServo = [str(ser) for ser in Device.DeviceManagerCLI.GetDeviceList(KCube.KCubeDCServo.DevicePrefix)] class Kinesis: @@ -323,6 +329,43 @@ def get_position(self) -> float: def stop(self): pass +class DCservo(Kinesis): + default_units = 'mm' + def __init__(self): + self._device: KCube.KCubeDCServo = None + + def connect(self, serial: int): + if serial in serialnumbers_dcServo: + self._device = KCube.KCubeDCServo.CreateKCubeDCServo(serial) + self._device.Connect(serial) + time.sleep(0.25) + self._device.StartPolling(250) + time.sleep(0.25) + self._device.EnableDevice() + time.sleep(0.25) + + if not self._device.IsSettingsInitialized(): + self._device.WaitForSettingsInitialized(10000) + assert self._device.IsSettingsInitialized() is True + + servo_config = self._device.LoadMotorConfiguration(serial) + logger.info(f"Servo Configuration: {servo_config}") + + def get_position(self): + return Decimal.ToDouble(self._device.get_DevicePosition()) + + def move_abs(self, position: float, callback=None): + self._device.MoveTo(Decimal(position), callback) + + def move_rel(self, position: float, callback=None): + self._device.MoveRelative(Generic.MotorDirection.Forward, Decimal(position), callback) + + def home(self, callback=None): + self._device.Home(callback) + + def stop(self): + self._device.Stop(0) + if __name__ == '__main__': controller = BrushlessDCMotor() controller.connect(serialnumbers_brushless[0]) @@ -338,4 +381,4 @@ def stop(self): print(f'Moving: {motor.is_moving}') print(motor.get_target_position()) - controller.close() \ No newline at end of file + controller.close()