Skip to content

Commit

Permalink
Merge pull request #7 from runrunm/main
Browse files Browse the repository at this point in the history
Adding Newport SMC100 motion controller to pymodaq_plugins_newport
  • Loading branch information
seb5g authored Sep 4, 2024
2 parents 70aa209 + d3408d7 commit 577d72d
Show file tree
Hide file tree
Showing 3 changed files with 321 additions and 107 deletions.
16 changes: 15 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pymodaq_plugins_newport (Newport Instruments)
.. image:: https://github.com/PyMoDAQ/pymodaq_plugins_newport/workflows/Upload%20Python%20Package/badge.svg
:target: https://github.com/PyMoDAQ/pymodaq_plugins_newport

PyMoDAQ plugin for instruments from Newport (Conex, ESP100, AG-CU8...)
PyMoDAQ plugin for instruments from Newport (Conex, ESP100, SMC100, AG-CU8...)


Authors
Expand All @@ -21,6 +21,7 @@ Authors
* Sebastien J. Weber
* David Bresteau ([email protected])
* Sébastien Quistrebert ([email protected])
* Bastien Bégon ([email protected])

Instruments
===========
Expand All @@ -33,6 +34,7 @@ Actuators
* **Newport_ESP100**: ESP100 motion controllers
* **AgilisSerial**: for controllers AG-UC8 and AG-UC2 tested with motorized mounts AG-M100N (no encoder)
* **XPS-Q8**: 8-axis Universal Motion Controller/Driver, ethernet
* **SMC100**: Single axis motion controller

Installation notes
==================
Expand All @@ -56,3 +58,15 @@ XPS-Q8
++++++

tested on Windows 11 with pymodaq >= 4.1.0.

SMC100
++++++

Tested with SMC100PP (stepper motor) controller using USB/RS232 connection and URS150 motorized rotation stage.
Installing `Newport SMC100 software <https://www.newport.com/f/smc100-single-axis-dc-or-stepper-motion-controller>`_ should provide all necessary drivers.

Operating System: Windows 11

PyMoDAQ version: 4.3.0 running in a conda environment with Python 3.11.9


Original file line number Diff line number Diff line change
@@ -1,41 +1,54 @@
# -*- coding: utf-8 -*-
"""
Created on Mon Jan 9 15:57:26 2023
@author: lb19g16
"""
from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, main # base class
from pymodaq.control_modules.move_utility_classes import comon_parameters_fun # common set of parameters for all actuators

from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, comon_parameters_fun, main, DataActuator
from pymodaq.utils.daq_utils import ThreadCommand # object used to send info back to the main thread
from pymodaq.utils.parameter import Parameter

from pymodaq_plugins_newport.hardware.smc100 import SMC100
import pyvisa

VISA_rm = pyvisa.ResourceManager()
infos = VISA_rm.list_resources_info()
com_ports = [infos[key].alias for key in infos.keys() if infos[key].alias is not None]
VISA_rm.close()
import pyvisa

rm = pyvisa.ResourceManager()
infos = rm.list_resources_info()
ports = [infos[key].interface_board_number for key in infos.keys()]
stage_nb = [1] # Works with 1 SMC100 controller (1 stage). Not tested with multiple controllers
rm.close()

class DAQ_Move_Newport_SMC100(DAQ_Move_base):
"""Plugin for the Template Instrument
""" 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.
This object inherits all functionality to communicate with PyMoDAQ Module through inheritance via DAQ_Move_base
It then implements the particular communication with the instrument
This plugin should be compatible with SMC100 controllers using USB/RS232 connection. Unit should be changed for
controlling linear actuators (currently set to degrees).
Tested with SMC100PP (stepper motor) controller and URS150 motorized rotation stage
Operating System: Windows 11
PyMoDAQ version: 4.3.0 running in a conda environment with Python 3.11.9
Installing Newport SMC100 software should install all necessary drivers (testing with manufacturer's software
should be done in the first place)
https://www.newport.com/f/smc100-single-axis-dc-or-stepper-motion-controller
Attributes:
-----------
controller: object
The particular object that allow the communication with the hardware, in general a python wrapper around the
hardware library
hardware library.
"""
_controller_units = 'mm'
is_multiaxes = True
axes_names = ['1'] # The axis list represents the number of smc controllers, indexed: first=1, second=2 etc.
_epsilon = 0.0001
params = [{'title': 'COM Port:', 'name': 'com_port', 'type': 'list', 'limits': com_ports, 'value': 'COM17'},
] + comon_parameters_fun(is_multiaxes, axes_names, epsilon=_epsilon)
_controller_units = '°'
is_multiaxes = False
_axis_names = ['1']
_epsilon = 0.01

params = [{'title': 'COM Port:', 'name': 'com_port', 'type': 'list', 'limits': ports},
{'title': 'Stage:', 'name': 'stage_nb', 'type': 'list', 'limits': stage_nb}
] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilon)

# print(params[0]['title'])

# _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

def ini_attributes(self):
self.controller: SMC100 = None
Expand All @@ -47,26 +60,30 @@ def get_actuator_value(self):
-------
float: The position obtained after scaling conversion.
"""

axis = int(self.settings.child('multiaxes', 'axis').value())
pos = self.controller.get_position(axis) # when writing your own plugin replace this line
print(f'pos is {pos}')
pos = self.controller.position
pos = self.get_position_with_scaling(pos)
return pos

def close(self):
"""Terminate the communication protocol"""
axis = int(self.settings.child('multiaxes', 'axis').value())
self.controller.close_communication(axis) # when writing your own plugin replace this line
self.controller.reset()
self.controller.close()

def commit_settings(self, param):
def commit_settings(self, param: Parameter, controller=None):
"""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
controller:
"""
# if param.name() == "com_port":
# if param.value():
# # self.controller.your_method_to_apply_this_param_change()
# self.controller.move_abs(10)
# else:
# pass
pass

def ini_stage(self, controller=None):
Expand All @@ -84,32 +101,35 @@ def ini_stage(self, controller=None):
False if initialization failed otherwise True
"""

self.ini_stage_init(old_controller=controller,
new_controller=SMC100())
if self.settings['multiaxes', 'multi_status'] == "Master":
self.controller.init_communication(
self.settings['com_port'])
axis = int(self.settings.child('multiaxes', 'axis').value())
info = self.controller.get_controller_infos(axis)
self.controller = self.ini_stage_init(old_controller=controller,
new_controller=SMC100(port=self.settings['com_port'],
dev_number=self.settings['stage_nb']))

info = "Initializing stage"
self.controller.initialize()
self.controller.homing() # Turns controller to REFERENCED state (solid green)

print(f"Connected to Newport stage {self.settings['stage_nb']} on COM{self.settings['com_port']}:"
f" {self.controller.idn}\n")
initialized = True
return info, initialized

def move_abs(self, value):
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
value = self.check_bound(value) # if user checked bounds, the defined bounds are applied here
self.target_value = value
value = self.set_position_with_scaling(value) # apply scaling if the user specified one

axis = int(self.settings['multiaxes', 'axis'])
self.controller.move_axis(axis=axis, pos=value) # when writing your own plugin replace this line
self.controller.move_abs(value) # when writing your own plugin replace this line
self.emit_status(ThreadCommand('Update_Status', ['Moving']))

def move_rel(self, value):
def move_rel(self, value: DataActuator):
""" Move the actuator to the relative target actuator value defined by value
Parameters
Expand All @@ -120,19 +140,22 @@ def move_rel(self, value):
self.target_value = value + self.current_position
value = self.set_position_relative_with_scaling(value)

axis = int(self.settings['multiaxes', 'axis'])
self.controller.move_axis('REL', axis=axis, pos=value) # when writing your own plugin replace this line
self.controller.move_rel(value) # when writing your own plugin replace this line
self.emit_status(ThreadCommand('Update_Status', ['Moving']))

def move_home(self):
"""Call the reference method of the controller"""
axis = int(self.settings['multiaxes', 'axis'])
self.controller.move_home(axis) # when writing your own plugin replace this line

self.controller.move_abs(.0) # when writing your own plugin replace this line
self.emit_status(ThreadCommand('Update_Status', ['Moving to position 0']))

def stop_motion(self):
"""Stop the actuator and emits move_done signal"""
axis = int(self.settings['multiaxes', 'axis'])
self.controller.stop_motion(axis) # when writing your own plugin replace this line

self.controller.stop() # when writing your own plugin replace this line
self.get_actuator_value()
self.emit_status(ThreadCommand('Update_Status', ['Motion stopped']))


if __name__ == '__main__':
main(__file__)
main(__file__)
Loading

0 comments on commit 577d72d

Please sign in to comment.