Skip to content

Commit

Permalink
creating a proper hardware wrapper for AGUC8
Browse files Browse the repository at this point in the history
  • Loading branch information
seb5g committed Nov 17, 2021
1 parent abcfc89 commit 38f12ed
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand All @@ -220,8 +218,6 @@ def move_Home(self):
self.current_position = 0.
self.target_position = 0.

self.poll_moving()


def stop_motion(self):
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -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 [email protected]: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},
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand All @@ -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()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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):
"""
Expand Down
146 changes: 146 additions & 0 deletions src/pymodaq_plugins_newport/hardware/AGUC8wrapper.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 38f12ed

Please sign in to comment.