Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,15 @@ The stages were alio's translation stages AI-CM-6000-XY

Installation instructions
=========================
The user as to install the drivers from ACS. He may also need to intialise the stage within the software with the buffer file provided by ACS. In this new release, the plugin is able to load the buffer file automatically during initialization. Users are encouraged to check the compatibility of their ACS hardware with the latest version of the plugin. Additionally, it is recommended to refer to the ACS documentation for any specific setup requirements.

* PyMoDAQ’s version: developed with PyMoDAQ 5.0.5
The user has to install the drivers from ACS. The homing program provided with the stages
must be stored in a buffer of the controller (buffer 1 by default, set with the
'Homing buffer' parameter of the plugin). The plugin never modifies the buffers of the
controller: the Home button runs the homing program and waits for its end, homing all the
axes at once. It is recommended to keep a full backup of the controller application, made
with the Application Wizard of SPiiPlus MMI Application Studio. Users are encouraged to check
the compatibility of their ACS hardware with the latest version of the plugin, and to refer
to the ACS documentation for any specific setup requirements..

* PyMoDAQ’s version: developed with PyMoDAQ 5.3.2
* Operating system’s version: Windows 11
* The plugin assumes that the acs drivers are installed.
7 changes: 2 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,24 @@ h5exporters = false # true if plugin contains custom h5 file exporters
scanners = false # true if plugin contains custom scan layout (daq_scan extensions)

[urls]
package-url = 'https://github.com/JMargueritatLab/pymodaq_plugins_acs_motion'
package-url = 'https://github.com/PyMoDAQ/pymodaq_plugins_acs_motion'

[project]
name = "pymodaq_plugins_acs_motion" #todo modify template by your plugin short name
name = "pymodaq_plugins_acs_motion"
description = 'This plugins interact with ACS motion controllers to drive stages.'
dependencies = [
"pymodaq>=5.3.2",
"pymodaq_utils>=5.3.1",
"pymodaq_gui>=5.3.0",
"pymodaq_data>=5.3.0",
"ACSpy",
#todo: list here all dependencies your package may have
]

authors = [
{name = "Jérémie Margueritat", email = "jeremie.margueritat@univ-lyon1.fr"},
#todo: list here all authors of your plugin
]
maintainers = [
{name = "Jérémie Margueritat", email = "jeremie.margueritat@univ-lyon1.fr"},
#todo: list here all maintainers of your plugin
]

# nottodo: leave everything below as is!
Expand Down
125 changes: 0 additions & 125 deletions src/pymodaq_plugins_acs_motion/app/custom_app_template.py

This file was deleted.

126 changes: 35 additions & 91 deletions src/pymodaq_plugins_acs_motion/daq_move_plugins/daq_move_AcsXY.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from pymodaq.control_modules.move_utility_classes import (DAQ_Move_base, comon_parameters_fun,
main, DataActuatorType, DataActuator)

from pymodaq_utils.utils import ThreadCommand # object used to send info back to the main thread
from pymodaq_gui.parameter import Parameter

try:
Expand All @@ -25,49 +24,39 @@ class DAQ_Move_AcsXY(DAQ_Move_base):
(It has been tested with USB to ethernet adapter).
Tested with ACS SPiiPlusEC controller and one drive UDMnt(2 axes).
The stages were alio's translation stages AI-CM-6000-XY.
PyMoDAQ version during the test was PyMoDAQ==5.0.5.
PyMoDAQ version during the test was PyMoDAQ==5.3.2.
The operating system used was Windows 11.
Installation instructions: ACS drivers must be installed from the manufacturer's.
They usually come with the controller and with a "buffer" file for coniguration.

The homing program provided with the stages must be stored in a buffer of the controller
(buffer 1 by default on the tested equipment, see the 'Homing buffer' parameter).
The plugin never modifies it.
The Home button runs this program and waits for its end: it homes all the axes at once.

Attributes:
-----------
controller: object
The particular object that allow the communication with the hardware, in general a python wrapper around the
hardware library.

# TODO add your particular attributes here if any

"""
is_multiaxes = True
# Configured for only two axiss, but can be changed to 8 axes.
_axis_names: Union[List[str], Dict[str, int]] = {'Axis0':0, 'Axis1':1}
# Here all axes are translations stages with the same unit if difefrent type of stages are used as for example one translation and one rotation the dict must be updated in consequences.
_controller_units: Union[str, List[str]] = {'Axis0': 'mm', 'Axis1': 'mm'}
# WARNING: Please refer to your specific stage to set a meaningful value. If you use different type of stages (ex: translation and rotation) it can be replaced by the appropriate epsilon value.
_epsilon: Union[float, List[float]] = 0.00001
data_actuator_type = DataActuatorType.DataActuator
# # wether you use the new data style for actuator otherwise set this
# as DataActuatorType.float (or entirely remove the line)
# At this step I will not use the new data dtyle it might be implemented in the future
# Configured for two axes, can be extended up to 8 axes.
_axis_names: Union[List[str], Dict[str, int]] = {'Axis0': 0, 'Axis1': 1}
# All axes are translation stages here; adapt per axis if stages of different types are used.
_controller_units: Union[str, Dict[str, str]] = {'Axis0': 'mm', 'Axis1': 'mm'}
# Tolerance used to decide that the target is reached; refer to your stages for a meaningful value.
_epsilons: Union[float, Dict[str, float]] = {'Axis0': 0.00001, 'Axis1': 0.00001}
data_actuator_type = DataActuatorType.DataActuator

params = [ {'title': 'Serial Number', 'name': 'serial_num', 'type': 'str',
'value': '', 'readonly': True,},
{'title': 'Buffer Number', 'name': 'buff_num', 'type': 'int',
{'title': 'Homing buffer', 'name': 'buff_num', 'type': 'int',
'value': 1, 'limits': [0, 9],}
] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilon)
# _epsilon is the initial default value for the epsilon parameter allowing pymodaq to know if the controller reached
# the target value. It is the developer responsibility to put here a meaningful value
] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names)

def ini_attributes(self):
# TODO declare the type of the wrapper (and assign it to self.controller) you're going to use for easy
# autocompletion
self.controller: Controller = None

#TODO declare here attributes you want/need to init with a default value
pass

def get_actuator_value(self):
"""Get the current value from the hardware with scaling conversion.

Expand All @@ -76,51 +65,27 @@ def get_actuator_value(self):
float: The position obtained after scaling conversion.
"""

pos = DataActuator(
data=self.controller.axes[self.axis_value].rpos,
unit=self.axis_unit)
pos = DataActuator(data=self.controller.axes[self.axis_value].rpos,
units=self.axis_unit)
pos = self.get_position_with_scaling(pos)
return pos

def user_condition_to_reach_target(self) -> bool:
""" Implement a condition for exiting the polling mechanism and specifying that the
target value has been reached

Returns
-------
bool: if True, PyMoDAQ considers the target value has been reached
"""
# TODO either delete this method if the usual polling is fine with you, but if need you can
# add here some other condition to be fullfilled either a completely new one or
# using or/and operations between the epsilon_bool and some other custom booleans
# for a usage example see DAQ_Move_brushlessMotor from the Thorlabs plugin
return True

def close(self):
"""Terminate the communication protocol"""
self.controller.disable_all()
self.controller.disconnect()
if self.controller is not None:
self.controller.disable_all()
self.controller.disconnect()

def commit_settings(self, param: Parameter):
"""Apply the consequences of a change of value in the detector settings
"""Apply the consequences of a change of value in the settings

Parameters
----------
param: Parameter
A given parameter (within detector_settings) whose value has been changed by the user
"""
## TODO for your custom plugin
if param.name() == 'axis':
self.get_actuator_value() # to update the current position of the axis
#self.axis_unit = 'mm'
# do this only if you can and if the units are not known beforehand, for instance
# if the motors connected to the controller are of different type (mm, µm, nm, , etc...)
# see BrushlessDCMotor from the thorlabs plugin for an exemple

elif param.name() == "a_parameter_you've_added_in_self.params":
self.controller.your_method_to_apply_this_param_change()
else:
pass
self.get_actuator_value() # update the current position of the newly selected axis

def ini_stage(self, controller=None):
"""Actuator communication initialization
Expand All @@ -144,54 +109,33 @@ def ini_stage(self, controller=None):
self.controller.connect() # any object that will control the stages
self.controller.enable_all() # enable all axes
self.settings['serial_num']=self.controller.serial_number()
self.controller.load_buffer(self.settings['buff_num']) # load the buffer file (it is needed to configure the controller)


info = "Controller connected and axis enabled"
initialized = True#self.controller.a_method_or_atttribute_to_check_if_init() # todo
initialized = all(axis.enabled for axis in self.controller.axes)
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) #if user checked bounds, the defined bounds are applied here
"""Move the actuator to the absolute target defined by value"""
value = self.check_bound(value)
self.target_value = value
value = self.set_position_with_scaling(value) # apply scaling if the user specified one
self.controller.axes[self.axis_value].ptp(value.value()) # when writing your own plugin replace this line
self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log']))
value = self.set_position_with_scaling(value)
self.controller.axes[self.axis_value].ptp(value.units_as(self.axis_unit).value())

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.axes[self.axis_value].ptpr(value.value())
self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log']))
"""Move the actuator by the relative amount defined by value"""
self.move_abs(self.current_value + value)

def move_home(self):
"""Call the reference method of the controller"""
self.move_abs(DataActuator(data=0, unit=self.axis_unit))

"""Run the homing program of the controller and wait for its end"""
self.controller.run_buffer(self.settings['buff_num'])
self.controller.wait_program_end(self.settings['buff_num'])
self.target_value = self.get_actuator_value()

def stop_motion(self):
"""Stop the actuator and emits move_done signal"""

## TODO for your custom plugin
raise NotImplemented # when writing your own plugin remove this line
self.controller.your_method_to_stop_positioning() # when writing your own plugin replace this line
self.emit_status(ThreadCommand('Update_Status', ['Some info you want to log']))
"""Stop the current axis and emit the move_done signal"""
self.controller.axes[self.axis_value].halt()
self.move_done()


if __name__ == '__main__':
Expand Down
Loading
Loading