-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
creating a proper hardware wrapper for AGUC8
- Loading branch information
Showing
4 changed files
with
162 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}, | ||
|
@@ -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() | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|