From 38f12edf2a25fcfed105a0864273bb0a583f4bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Weber?= Date: Wed, 17 Nov 2021 17:16:21 +0100 Subject: [PATCH] creating a proper hardware wrapper for AGUC8 --- .../daq_move_Newport_AGUC8.py | 4 - .../daq_move_Newport_AGUC8low.py | 122 ++------------- .../daq_move_Newport_ESP100.py | 5 - .../hardware/AGUC8wrapper.py | 146 ++++++++++++++++++ 4 files changed, 162 insertions(+), 115 deletions(-) create mode 100644 src/pymodaq_plugins_newport/hardware/AGUC8wrapper.py diff --git a/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_AGUC8.py b/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_AGUC8.py index da6b708..fc5e0bb 100644 --- a/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_AGUC8.py +++ b/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_AGUC8.py @@ -199,8 +199,6 @@ def move_Rel(self, relative_move): # if status != 'TE0': # logger.warning(f'wrong return from controller {status}') self.flush_read() - - self.poll_moving() except SerialTimeoutException as e: logger.warning(str(e)) @@ -220,8 +218,6 @@ def move_Home(self): self.current_position = 0. self.target_position = 0. - self.poll_moving() - def stop_motion(self): """ diff --git a/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_AGUC8low.py b/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_AGUC8low.py index 43e9e68..9fb2fc9 100644 --- a/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_AGUC8low.py +++ b/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_AGUC8low.py @@ -1,54 +1,22 @@ from pymodaq.daq_move.utility_classes import DAQ_Move_base, comon_parameters, main from pymodaq.daq_utils.daq_utils import ThreadCommand, getLineInfo, set_logger, get_module_name from easydict import EasyDict as edict -from instruments.newport.agilis import AGUC2 -import pyvisa -from serial.serialwin32 import SerialTimeoutException +from pymodaq_plugins_newport.hardware.AGUC8wrapper import AGUC8, COMPORTS logger = set_logger(get_module_name(__file__)) -""" -Installation ------------- -Install Newport AG-UC2-UC8 applet available here: https://www.newport.com/p/AG-UC8 - -This plugin use the instrumentkit library. Currently the version proposed on pypi 0.6.0 -does not include the newport/agilis.py file that we are interrested in. We recommand to -install the library from git (not with pip), as it is explained in this page: -https://github.com/Galvant/InstrumentKit - -$ git clone git@github.com:Galvant/InstrumentKit.git -$ cd InstrumentKit -$ python setup.py install - -Troubleshooting ---------------- -It happens that the plugin initialize correctly (green light in a daq_move) but still -sending a move order will have no effect. -Try to load the AGUC2 class and launch an order in an independent script and try again -with pymodaq. We do not know why but it seems to solve the problem... - -""" - class DAQ_Move_Newport_AGUC8low(DAQ_Move_base): """ """ _controller_units = 'step' is_multiaxes = True - channel_names = [1, 2, 3, 4] - axis_names = [1, 2] - - # find available COM ports - visa_rm = pyvisa.ResourceManager() - infos = visa_rm.list_resources_info() - ports = [] - for k in infos.keys(): - ports.append(infos[k].alias) - port = 'COM9' if 'COM9' in ports else ports[0] if len(ports) > 0 else '' + channel_names = AGUC8.channel_names + axis_names = AGUC8.axis_names + port = 'COM9' if 'COM9' in COMPORTS else COMPORTS[0] if len(COMPORTS) > 0 else '' params = [ - {'title': 'COM Port:', 'name': 'com_port', 'type': 'list', 'limits': ports, 'value': port}, + {'title': 'COM Port:', 'name': 'com_port', 'type': 'list', 'limits': COMPORTS, 'value': port}, {'title': 'Firmware:', 'name': 'firmware', 'type': 'str', 'value': ''}, {'title': 'Channel:', 'name': 'channel', 'type': 'list', 'limits': channel_names}, {'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': axis_names}, @@ -71,9 +39,6 @@ def __init__(self, parent=None, params_state=None): self.current_position = 0 self.target_position = 0 - # we do not poll the moving since we consider an actuator without encoder - # self.settings.child('epsilon').setValue(1) - def ini_stage(self, controller=None): """ Actuator communication initialization @@ -111,34 +76,16 @@ def ini_stage(self, controller=None): else: self.controller = controller else: # Master stage - # self.controller = AGUC2.open_serial( - # port=self.settings.child('com_port').value(), - # baud=921600 - # ) - self.controller = self.visa_rm.open_resource(self.settings.child('com_port').value(), - baud_rate=921600) + self.controller = AGUC8() + self.controller.open(self.settings.child('com_port').value()) + info = self.controller.get_infos() + self.controller.select_channel(self.settings.child('channel').value()) - self.controller.read_termination = self.controller.CR + self.controller.LF - self.controller.write_termination = self.controller.CR + self.controller.LF - self.controller.timeout = 10 - self.flush_read() - - info = self.controller.query('VE') - self.flush_read() + info = self.controller.get_infos() self.settings.child('firmware').setValue(info) - # Select the good channel. - channel = self.settings.child('channel').value() - order = "CC" + str(channel) - self.controller.write(order) - self.flush_read() - - # Configure the sleep time which is a time delay in second after each order. - # The setter of the library does not work that is why we use this dirty way - # by calling directly a private attribute. - self.controller._sleep_time = self.settings.child('sleep_time').value() - - self.status.info = f"Actuator AGUC8 initialized on com port {self.settings.child('com_port').value()}" + + self.status.info = info self.status.controller = self.controller self.status.initialized = True @@ -192,43 +139,15 @@ def move_Rel(self, relative_move): relative_move = self.set_position_relative_with_scaling(relative_move) self.target_position = relative_move + self.current_position - axis_number = self.settings.child('axis').value() - order = f'{axis_number:.0f}PR{relative_move:.0f}' - ready = False - while not ready: - try: - - self.controller.write(order) - # status = self.controller.ag_query('TE') - # if status != 'TE0': - # logger.warning(f'wrong return from controller {status}') - self.flush_read() - ready = True - - except SerialTimeoutException as e: - logger.warning(str(e)) - - self.poll_moving() - - def flush_read(self): - while True: - try: - ret = self.controller.read() - print(ret) - except: - break + self.controller.move_rel(self.settings.child('axis').value(), int(relative_move)) def move_Home(self): """ """ - self.current_position = 0. self.target_position = 0. - self.poll_moving() - - def stop_motion(self): """ Stop an ongoing move. @@ -241,22 +160,13 @@ def commit_settings(self, param): """ Called after a param_tree_changed signal from DAQ_Move_main. """ - if param.name() == "sleep_time": - self.controller._sleep_time = param.value() - elif param.name() == 'channel': - self.controller.write(f'CC{param.value()}') - status = self.controller.query('TE') - if status != 'TE0': - logger.warning(f'wrong return from controller {status}') - self.flush_read() - channel = self.controller.query('CC?') - param.setValue(int(channel[2:])) - + if param.name() == 'channel': + self.controller.select_channel(param.value()) + param.setValue(int(self.controller.read_channel())) def close(self): """ Terminate the communication protocol. - Not implemented. """ self.controller.close() diff --git a/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_ESP100.py b/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_ESP100.py index 763fa5e..e357b04 100644 --- a/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_ESP100.py +++ b/src/pymodaq_plugins_newport/daq_move_plugins/daq_move_Newport_ESP100.py @@ -164,9 +164,6 @@ def move_Abs(self,position): position = self.set_position_with_scaling(position) out = self.controller.move_axis('ABS', self._axis, position) - self.poll_moving() - - def move_Rel(self,position): """ Make the hardware relative move of the Piezo instrument from the given position after thread command signal was received in DAQ_Move_main. @@ -187,8 +184,6 @@ def move_Rel(self,position): position = self.set_position_relative_with_scaling(position) out = self.controller.move_axis('REL', self._axis, pos) - QThread.msleep(50) # to make sure the closed loop converged - self.poll_moving() def move_Home(self): """ diff --git a/src/pymodaq_plugins_newport/hardware/AGUC8wrapper.py b/src/pymodaq_plugins_newport/hardware/AGUC8wrapper.py new file mode 100644 index 0000000..ef660dc --- /dev/null +++ b/src/pymodaq_plugins_newport/hardware/AGUC8wrapper.py @@ -0,0 +1,146 @@ +import time +import pyvisa +from threading import Lock +from pyvisa.errors import VisaIOError + +from pymodaq.daq_utils.daq_utils import set_logger, get_module_name + +logger = set_logger(get_module_name(__file__)) +visa_rm = pyvisa.ResourceManager() + +_infos = visa_rm.list_resources_info() +COMPORTS = [] +for k in _infos.keys(): + COMPORTS.append(_infos[k].alias) + +lock = Lock() + + +class AGUC8: + channel_names = [1, 2, 3, 4] + axis_names = [1, 2] + + def __init__(self): + self._controller = None + self._info = None + self._timeout_wait_isready_ms = 10000 + + def open(self, com_port): + if com_port in COMPORTS: + self._controller = visa_rm.open_resource(com_port, baud_rate=921600) + time.sleep(1) + + self._controller.read_termination = self._controller.CR + self._controller.LF + self._controller.write_termination = self._controller.CR + self._controller.LF + self._controller.timeout = 10 + + def get_infos(self): + if self._controller is not None: + if self._info is None: + self._info = self.query('VE') + return self._info + + def reset(self): + # todo test it + self.write('RS') + + def stop(self, axis: int): + # todo test it + command = f'{axis:.0f}ST' + self.write(command) + + def select_channel(self, channel_index: int): + order = "CC" + str(channel_index) + self.write(order) + + def get_channel(self): + #todo test it + channel = self.query('CC?') + return int(channel[2:]) + + def get_axis_isready(self, axis): + command = f'{axis:.0f}TS' + status = self.query(command) + return status == 0 + + def move_rel(self, axis: int, steps: int): + time_start = time.perf_counter() + while not self.get_axis_isready(axis): + time.sleep(0.05) + if time.perf_counter() - time_start > self._timeout_wait_isready / 1000: + self.stop(axis) + raise TimeoutError(f"axis {axis} could'nt be ready after an elapsed time of" + f" {self._timeout_wait_isready} ms") + + order = f'{axis:.0f}PR{steps:.0f}' + self.write(order) + + def counter_to_zero(self, axis): + command = f'{axis:.0f}ZP' + self.write(command) + + def get_step_counter(self, axis): + """ + Returns the number of accumulated steps in forward direction minus the number of steps in backward direction + since powering the controller or since the last ZP (zero position) command + """ + command = f'{axis:.0f}TP' + steps = self.query(command) + return steps + + def is_at_limits(self): + """ + check if both axis of current channel are at the limit (if any) + Returns + ------- + bool: status of axis 1 + bool: status of axis 2 + """ + command = f'PH' + ret = self.query(command) + if ret == 'PH0': + return False, False + elif ret == 'PH1': + return True, False + elif ret == 'PH2': + return False, True + elif ret == 'PH3': + return True, True + + def close(self): + self._controller.close() + + def query(self, command: str): + value = None + try: + lock.acquire() + value = self._controller.query(command) + ret = self._controller.query('TE') + if ret != 0: + logger.warning(f'Error code {ret} returned from the query of the command {command}') + except VisaIOError as e: + logger.debug(str(e)) + finally: + lock.release() + return value + + def write(self, command: str): + try: + lock.acquire() + self._controller.write(command) + ret = self._controller.query('TE') + if ret != 0: + logger.warning(f'Error code {ret} returned from the write of the command {command}') + except VisaIOError as e: + logger.debug(str(e)) + finally: + lock.release() + + def flush_read(self): + while True: + try: + ret = self._controller.read() + print(f'Flushing returned: {ret}') + except: + break +