Commit 2cd10321 authored by Cyril Guilloud's avatar Cyril Guilloud
Browse files

communication of PI E727 + HW tests + comments + ignore error on STP

parent 7fd531b0
Pipeline #49328 passed with stages
in 154 minutes and 48 seconds
...@@ -126,6 +126,9 @@ class _Group(object): ...@@ -126,6 +126,9 @@ class _Group(object):
@property @property
def position_with_reals(self): def position_with_reals(self):
"""
???
"""
return self._dial_or_position("position", with_reals=True) return self._dial_or_position("position", with_reals=True)
@property @property
...@@ -133,6 +136,9 @@ class _Group(object): ...@@ -133,6 +136,9 @@ class _Group(object):
return self._dial_or_position("dial", with_reals=True) return self._dial_or_position("dial", with_reals=True)
def _dial_or_position(self, attr, with_reals=False): def _dial_or_position(self, attr, with_reals=False):
"""
???
"""
positions_dict = dict() positions_dict = dict()
if with_reals: if with_reals:
......
...@@ -41,7 +41,8 @@ responses to them are). ...@@ -41,7 +41,8 @@ responses to them are).
class PI_E51X(pi_gcs.Communication, pi_gcs.Recorder, Controller): class PI_E51X(pi_gcs.Communication, pi_gcs.Recorder, Controller):
""" Base class for E517 and E518 """
Base class for E517 and E518
""" """
CHAN_LETTER = {1: "A", 2: "B", 3: "C"} CHAN_LETTER = {1: "A", 2: "B", 3: "C"}
...@@ -96,6 +97,9 @@ class PI_E51X(pi_gcs.Communication, pi_gcs.Recorder, Controller): ...@@ -96,6 +97,9 @@ class PI_E51X(pi_gcs.Communication, pi_gcs.Recorder, Controller):
# acceleration is not mandatory in config # acceleration is not mandatory in config
self.axis_settings.config_setting["acceleration"] = False self.axis_settings.config_setting["acceleration"] = False
# should have been set at init of inherited classes.
log_debug(self, "model=%s", self.model)
def close(self): def close(self):
""" """
Called at session exit. 6 times ??? Called at session exit. 6 times ???
...@@ -106,7 +110,7 @@ class PI_E51X(pi_gcs.Communication, pi_gcs.Recorder, Controller): ...@@ -106,7 +110,7 @@ class PI_E51X(pi_gcs.Communication, pi_gcs.Recorder, Controller):
def initialize_hardware(self): def initialize_hardware(self):
""" """
Called once per controller at first axis use. Called once per controller at first use of any of the axes.
""" """
# Initialize socket communication. # Initialize socket communication.
self.com_initialize() self.com_initialize()
...@@ -119,6 +123,8 @@ class PI_E51X(pi_gcs.Communication, pi_gcs.Recorder, Controller): ...@@ -119,6 +123,8 @@ class PI_E51X(pi_gcs.Communication, pi_gcs.Recorder, Controller):
def initialize_axis(self, axis): def initialize_axis(self, axis):
""" """
Called at first access to <axis> (eg: __info__())
- Reads specific config - Reads specific config
- Adds specific methods - Adds specific methods
- Switches piezo to ONLINE mode so that axis motion can be caused - Switches piezo to ONLINE mode so that axis motion can be caused
......
...@@ -6,72 +6,82 @@ ...@@ -6,72 +6,82 @@
# Distributed under the GNU LGPLv3. See LICENSE for more info. # Distributed under the GNU LGPLv3. See LICENSE for more info.
from bliss.controllers.motor import Controller from bliss.controllers.motor import Controller
from bliss.common.utils import object_method from bliss.common.utils import object_attribute_get, object_attribute_set, object_method
from bliss.common.axis import AxisState from bliss.common.axis import AxisState
from bliss.common.logtools import log_debug, log_error from bliss.common.logtools import log_debug, log_error, log_info
from bliss import global_map from bliss import global_map
from . import pi_gcs from . import pi_gcs
from bliss.comm.util import TCP
""" class PI_E727(pi_gcs.Communication, pi_gcs.Recorder, Controller):
Bliss controller for ethernet PI E727 piezo controller. """
""" Bliss controller for ethernet PI E727 piezo controller.
"""
model = None
class PI_E727(Controller):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# Called at session startup
# No hardware access
pi_gcs.Communication.__init__(self)
pi_gcs.Recorder.__init__(self)
Controller.__init__(self, *args, **kwargs) Controller.__init__(self, *args, **kwargs)
self.cname = "E727"
# Init of controller. # Init of controller.
def initialize(self): def initialize(self):
""" """
Controller intialization : open a single socket for all 3 axes. Controller intialization.
Called at session startup.
Called Only once per controller even if more than
one axis is declared in config.
""" """
# acceleration is not mandatory in config # acceleration is not mandatory in config
self.axis_settings.config_setting["acceleration"] = False self.axis_settings.config_setting["acceleration"] = False
self.trace("controller initialize") # model of the controller.
self.host = self.config.get("host") self.model = "E727"
self.trace("opening socket") log_debug(self, "model=%s", self.model)
self.sock = pi_gcs.get_pi_comm(self.config, TCP)
global_map.register(self, children_list=[self.sock])
def finalize(self): def close(self):
""" """
Close the controller socket. Called at session exit. many times ???
""" """
# not called at end of device server ??? :( self.com_close()
# called on a new axis creation ???
self.trace("controller finalize")
try:
self.trace("closing socket")
self.sock.close()
except:
pass
def trace(self, str): def initialize_hardware(self):
log_debug(self, "{s:{c}<{n}}".format(s=str, n=80, c="-")) """
Called once per controller at first use of any of the axes.
# Init of each axis. NB: not initialized if accessing to the controller before the axis:
def initialize_axis(self, axis): ex: LILAB [1]: napix.controller.command("CCL 1 advanced")
self.trace("axis initialization") !!! === AttributeError: 'NoneType' object has no attribute 'lock' === !!!
"""
# Initialize socket communication.
self.com_initialize()
def initialize_hardware_axis(self, axis):
"""
Called once per axis at first use of the axis
"""
pass
def initialize_axis(self, axis):
"""
Called at first access to <axis> (eg: __info__())
"""
axis.channel = axis.config.get("channel", int) axis.channel = axis.config.get("channel", int)
# check communication # check communication
try: _ans = self.get_identifier(axis)
_ans = self.get_identifier(axis, 0.1)
except Exception as ex:
_str = '%r\nERROR on "%s": switch on the controller' % (ex, self.host)
# by default, an exception will be raised
log_error(self, _str)
# Enables the closed-loop. # Enables the closed-loop.
# To be imporved.
self._set_closed_loop(axis, True) self._set_closed_loop(axis, True)
# supposed that we are on target on init
axis._last_on_target = True
def initialize_encoder(self, encoder): def initialize_encoder(self, encoder):
pass pass
...@@ -85,17 +95,31 @@ class PI_E727(Controller): ...@@ -85,17 +95,31 @@ class PI_E727(Controller):
def set_off(self, axis): def set_off(self, axis):
pass pass
"""
POSITION
"""
def read_position(self, axis): def read_position(self, axis):
_ans = self._get_target_pos(axis) """
log_debug(self, "read_position = %f" % _ans) Return position of the axis.
return _ans After movement is finished, return the target position.
"""
if axis._last_on_target:
_pos = self._get_target_pos(axis)
log_debug(self, "position read : %g" % _pos)
else:
# if moving return real position
_pos = self._get_pos(axis)
return _pos
def read_encoder(self, encoder): def read_encoder(self, encoder):
_ans = self._get_pos(axis) _ans = self._get_pos(encoder.axis)
log_debug(self, "read_position measured = %f" % _ans) log_debug(self, "read_position measured = %f" % _ans)
return _ans return _ans
""" VELOCITY """ """
VELOCITY
"""
def read_velocity(self, axis): def read_velocity(self, axis):
return self._get_velocity(axis) return self._get_velocity(axis)
...@@ -103,15 +127,16 @@ class PI_E727(Controller): ...@@ -103,15 +127,16 @@ class PI_E727(Controller):
def set_velocity(self, axis, new_velocity): def set_velocity(self, axis, new_velocity):
log_debug(self, "set_velocity new_velocity = %f" % new_velocity) log_debug(self, "set_velocity new_velocity = %f" % new_velocity)
_cmd = "VEL %s %f" % (axis.channel, new_velocity) _cmd = "VEL %s %f" % (axis.channel, new_velocity)
self.send_no_ans(_cmd) self.command(_cmd)
self.check_error(_cmd)
return self.read_velocity(axis) return self.read_velocity(axis)
""" STATE """ """
STATE
"""
def state(self, axis): def state(self, axis):
self.trace("axis state") # self.trace("axis state")
if self._get_closed_loop_status(axis): if self._get_closed_loop_status(axis):
if self._get_on_target_status(axis): if self._get_on_target_status(axis):
return AxisState("READY") return AxisState("READY")
...@@ -120,7 +145,9 @@ class PI_E727(Controller): ...@@ -120,7 +145,9 @@ class PI_E727(Controller):
else: else:
raise RuntimeError("closed loop disabled") raise RuntimeError("closed loop disabled")
""" MOVEMENTS """ """
MOVEMENTS
"""
def prepare_move(self, motion): def prepare_move(self, motion):
pass pass
...@@ -128,106 +155,53 @@ class PI_E727(Controller): ...@@ -128,106 +155,53 @@ class PI_E727(Controller):
def start_one(self, motion): def start_one(self, motion):
log_debug(self, "start_one target_pos = %f" % motion.target_pos) log_debug(self, "start_one target_pos = %f" % motion.target_pos)
# the controller latches the previous error
self.clear_error()
axis = motion.axis axis = motion.axis
_cmd = "MOV %s %g" % (axis.channel, motion.target_pos) _cmd = f"MOV {axis.channel} {motion.target_pos}"
self.send_no_ans(_cmd) self.command(_cmd)
self.check_error(_cmd)
def stop(self, axis): def stop(self, axis):
log_debug(self, "stop requested") """
self.send_no_ans("STP %s" % (axis.channel)) * STP -> stop asap
* 24 -> stop asap
""" COMMUNICATIONS""" * HLT -> stop smoothly
* Copy of current position into target position at stop
def send(self, cmd, timeout=None): * all 3 commands generate (by default) a 'Disable Error 10'.
_cmd = self._append_eoc(cmd) (This can be disabled via parameter 0x0E000301)
_ans = self.sock.write_readline(_cmd.encode(), timeout=timeout).decode() """
_ans = self._remove_eoc(_ans) log_debug(self, "stop (HLT) requested")
return _ans self.command("HLT %s" % (axis.channel))
def clear_error(self):
self._get_error()
def check_error(self, cmd):
# Check error code
(_err_nb, _err_str) = self._get_error()
if _err_nb != 0:
_str = 'ERROR on cmd "%s": #%d(%s)' % (cmd, _err_nb, _err_str)
# by default, an exception will be raised
log_error(self, _str)
raise RuntimeError(_str)
def send_no_ans(self, cmd):
_cmd = self._append_eoc(cmd)
self.sock.write(_cmd.encode())
@object_method(types_info=("None", "None"))
def raw_flush(self, axis):
self.sock.flush()
""" RAW COMMANDS """
def raw_write(self, cmd):
_cmd = self._append_eoc(cmd)
self.sock.write(_cmd.encode())
def raw_write_read(self, cmd):
_cmd = self._append_eoc(cmd)
return self.sock.write_readline(_cmd.encode()).decode()
def raw_write_readlines(self, cmd, lines):
_cmd = self._append_eoc(cmd)
ans = "\n".join(
[
r.decode().strip()
for r in self.sock.write_readlines(_cmd.encode(), lines)
]
)
_ans = self._remove_eoc(ans)
return _ans
def _append_eoc(self, cmd):
_cmd = cmd.strip()
if not _cmd.endswith("\n"):
_cmd = cmd + "\n"
log_debug(self, ">>>> %s" % (_cmd.strip("\n")))
return _cmd
def _remove_eoc(self, ans):
_ans = ans.strip()
log_debug(self, "<<<< %s" % _ans)
return _ans
""" """
E727 specific E727 specific
""" """
@object_method(types_info=("None", "str")) @object_method(types_info=("None", "str"))
def get_identifier(self, axis, timeout=None): def get_identifier(self, axis):
return self.send("IDN?", timeout) return self.command("IDN?")
@object_method(types_info=("None", "float"))
def get_voltage(self, axis): def get_voltage(self, axis):
""" Return voltage read from controller.""" """ Return voltage read from controller."""
_ans = self.send("SVA?") _ans = self.command(f"SVA? {axis.channel}")
_voltage = float(_ans.split("=")[1]) _voltage = float(_ans)
return _voltage return _voltage
@object_method(types_info=("None", "float"))
def get_output_voltage(self, axis):
""" Return output voltage read from controller. """
return float(self.command(f"VOL? {axis.channel}"))
def set_voltage(self, axis, new_voltage): def set_voltage(self, axis, new_voltage):
""" Set Voltage to the controller.""" """ Set Voltage to the controller."""
_cmd = "SVA %s %g" % (axis.channel, new_voltage) _cmd = "SVA %s %g" % (axis.channel, new_voltage)
self.send_no_ans(_cmd) self.command(_cmd)
self.check_error(_cmd)
def _get_velocity(self, axis): def _get_velocity(self, axis):
""" """
Return velocity taken from controller. Return velocity taken from controller.
""" """
_ans = self.send("VEL? %s" % (axis.channel)) _ans = self.command(f"VEL? {axis.channel}")
_velocity = float(_ans.split("=")[1]) _velocity = float(_ans)
return _velocity return _velocity
...@@ -235,57 +209,50 @@ class PI_E727(Controller): ...@@ -235,57 +209,50 @@ class PI_E727(Controller):
""" """
Return real position read by capacitive sensor. Return real position read by capacitive sensor.
""" """
_ans = self.send("POS? %s" % (axis.channel)) _ans = self.command(f"POS? {axis.channel}")
_pos = float(_ans.split("=")[1]) _pos = float(_ans)
return _pos return _pos
""" ON TARGET """ """
CLOSED LOOP
"""
def _get_target_pos(self, axis): def _get_target_pos(self, axis):
""" """
Return last target position (setpoint value). Return last target position (setpoint value).
""" """
_ans = self.send("MOV? %s" % (axis.channel)) _ans = self.command(f"MOV? {axis.channel}")
# _ans should looks like "1=-8.45709419e+01" _pos = float(_ans)
_pos = float(_ans.split("=")[1])
return _pos return _pos
def _get_on_target_status(self, axis): def _get_on_target_status(self, axis):
_ans = self.send("ONT? %s" % (axis.channel)) """
Return 'On-target status' (ONT? command) indicating
_status = _ans.split("=")[1] if movement is finished and measured position is within
on-target window.
if _status == "1": """
return True last_on_target = bool(int(self.command(f"ONT? {axis.channel}")))
elif _status == "0": axis._last_on_target = last_on_target
return False return last_on_target
else:
log_error(self, "ERROR on _get_on_target_status, _ans=%r" % _ans)
return -1
""" CLOSED LOOP"""
def _get_closed_loop_status(self, axis): def _get_closed_loop_status(self, axis):
_ans = self.send("SVO? %s" % (axis.channel)) _status = self.command(f"SVO? {axis.channel}")
_status = _ans.split("=")[1]
if _status == "1": if _status == "1":
return True return True
elif _status == "0":
if _status == "0":
return False return False
else:
log_error(self, "ERROR on _get_closed_loop_status, _ans=%r" % _ans) log_error(self, "ERROR on _get_closed_loop_status, _status=%r" % _status)
return -1 return -1
def _set_closed_loop(self, axis, state): def _set_closed_loop(self, axis, state):
if state: if state:
_cmd = "SVO %s 1" % (axis.channel) _cmd = f"SVO {axis.channel} 1"
else: else:
_cmd = "SVO %s 0" % (axis.channel) _cmd = f"SVO {axis.channel} 0"
self.send_no_ans(_cmd) self.command(_cmd)
self.check_error(_cmd)
@object_method(types_info=("None", "None")) @object_method(types_info=("None", "None"))
def open_loop(self, axis): def open_loop(self, axis):
...@@ -295,45 +262,49 @@ class PI_E727(Controller): ...@@ -295,45 +262,49 @@ class PI_E727(Controller):
def close_loop(self, axis): def close_loop(self, axis):
self._set_closed_loop(axis, True) self._set_closed_loop(axis, True)
def _get_error(self): @object_attribute_get(type_info="bool")
# Does not use send() to be able to call _get_error in send(). def get_closed_loop(self, axis):
_error_number = int(self.raw_write_read("ERR?")) return self._get_closed_loop_status(axis)
_error_str = pi_gcs.get_error_str(int(_error_number))
return (_error_number, _error_str) """
INFO
"""
def __info__(self): @object_attribute_get(type_info="str")
_tab = 30 def get_model(self, axis):
return self.model
(_err_nb, _err_str) = self._get_error() def get_id(self, axis):
_txt = "%*s: %d (%s)\n" % (_tab, "Last Error", _err_nb, _err_str) """
Return controller identifier.
"""
return self.command("*IDN?")
_txt = _txt + "%*s:\n%s\n" % ( def get_axis_info(self, axis):
_tab, """
"Communication parameters", Return Controller specific info about <axis> for user.