import logging
import asyncio
logger = logging.getLogger(__name__)
def _make_do(cmd, doc=None):
def f(self, xx=None, *nn):
self.do(cmd, xx, *nn)
if doc is not None:
f.__doc__ = doc
return f
def _make_ask(cmd, doc=None, conv=int):
assert cmd.endswith("?")
async def f(self, xx=None, *nn):
ret = await self.ask(cmd, xx, *nn)
ret = conv(ret)
return ret
if doc is not None:
f.__doc__ = doc
return f
[docs]class NewFocus8742Protocol:
"""New Focus/Newport 8742 Driver.
Four Channel Picomotor Controller and Driver Module, Open-Loop, 4 Channel.
https://www.newport.com/p/8742
"""
poll_interval = .01
[docs] def fmt_cmd(self, cmd, xx=None, *nn):
"""Format a command.
Args:
cmd (str): few-letter command
xx (int, optional for some commands): Motor channel
nn (multiple int, optional): additional parameters
"""
if xx is not None:
cmd = "{:d}".format(xx) + cmd
if nn:
cmd += ", ".join("{:d}".format(n) for n in nn)
return cmd
[docs] def do(self, cmd, xx=None, *nn):
"""Format and send a command to the device
See Also:
:meth:`fmt_cmd`: for the formatting and additional
parameters.
"""
cmd = self.fmt_cmd(cmd, xx, *nn)
assert len(cmd) < 64
logger.debug("do %s", cmd)
self._writeline(cmd)
[docs] async def ask(self, cmd, xx=None, *nn):
"""Execute a command and return a response.
The command needs to include the final question mark.
See Also:
:meth:`fmt_cmd`: for the formatting and additional
parameters.
"""
assert cmd.endswith("?")
self.do(cmd, xx, *nn)
ret = await self._readline()
logger.debug("ret %s", ret)
return ret
def _writeline(self, cmd):
raise NotImplemented
async def _readline(self):
raise NotImplemented
identify = _make_ask("*IDN?",
"""Get product identification string.
This query will cause the instrument to return a unique
identification string. This similar to the Version (VE) command but
provides more information. In response to this command the
controller replies with company name, product model name, firmware
version number, firmware build date, and controller serial number.
No two controllers share the same model name and serial numbers,
therefore this information can be used to uniquely identify a
specific controller.""", conv=str)
recall = _make_do("*RCL",
"""Recall settings.
This command restores the controller working parameters from values
saved in its nonvolatile memory. It is useful when, for example,
the user has been exploring and changing parameters (e.g.,
velocity) but then chooses to reload from previously stored,
qualified settings. Note that “\*RCL 0” command just restores the
working parameters to factory default settings. It does not change
the settings saved in EEPROM.""")
reset = _make_do("*RST",
"""Reset.
This command performs a “soft” reset or reboot of the controller
CPU. Upon restart the controller reloads parameters (e.g., velocity
and acceleration) last saved in non-volatile memory. Note that upon
executing this command, USB and Ethernet communication will be
interrupted for a few seconds while the controller re-initializes.
Ethernet communication may be significantly delayed (~30 seconds)
in reconnecting depending on connection mode (Peer-to-peer, static
or dynamic IP mode) as the PC and controller are negotiating TCP/IP
communication.""")
abort = _make_do("AB",
"""Abort motion.
This command is used to instantaneously stop any motion that is in
progress. Motion is stopped abruptly. For stop with deceleration
see ST command which uses programmable acceleration/deceleration
setting.""")
set_acceleration = _make_do("AC",
"""Set acceleration.
This command is used to set the acceleration value for an axis. The
acceleration setting specified will not have any effect on a move
that is already in progress. If this command is issued when an
axis’ motion is in progress, the controller will accept the new
value but it will use it for subsequent moves only. """)
get_acceleration = _make_ask("AC?",
"""Get acceleration.
This command is used to query the acceleration value for an axis.""")
set_home = _make_do("DH",
"""Set home position.
This command is used to define the “home” position for an axis. The
home position is set to 0 if this command is issued without “nn”
value. Upon receipt of this command, the controller will set the
present position to the specified home position. The move to
absolute position command (PA) uses the “home” position as
reference point for moves.""")
get_home = _make_ask("DH?",
"""Get home position.
This command is used to query the home position value for an
axis.""")
check_motor = _make_do("MC",
"""Motor check.
This command scans for motors connected to the controller, and sets
the motor type based on its findings. If the piezo motor is found
to be type ‘Tiny’ then velocity (VA) setting is automatically
reduced to 1750 if previously set above 1750. To accomplish this
task, the controller commands each axis to make a one-step move in
the negative direction followed by a similar step in the positive
direction. This process is repeated for all the four axes starting
with the first one. If this command is issued when an axis is
moving, the controller will generate “MOTION IN PROGRESS” error
message.""")
done = _make_ask("MD?",
"""Motion done query.
This command is used to query the motion status for an axis.""")
move = _make_do("MV",
"""Indefinite move.
This command is used to move an axis indefinitely. If this command
is issued when an axis’ motion is in progress, the controller will
ignore this command and generate “MOTION IN PROGRESS” error
message. Issue a Stop (ST) or Abort (AB) motion command to
terminate motion initiated by MV""")
set_position = _make_do("PA",
"""Target position move command.
This command is used to move an axis to a desired target (absolute)
position relative to the home position defined by DH command. Note
that DH is automatically set to 0 after system reset or a power
cycle. If this command is issued when an axis’ motion is in
progress, the controller will ignore this command and generate
“MOTION IN PROGRESS” error message. The direction of motion and
number of steps needed to complete the motion will depend on where
the motor count is presently at before the command is issued. Issue
a Stop (ST) or Abort (AB) motion command to terminate motion
initiated by PA""")
get_position = _make_ask("PA?",
"""Get target position.
This command is used to query the target position of an axis.""")
set_relative = _make_do("PR",
"""Relative move.
This command is used to move an axis by a desired relative
distance. If this command is issued when an axis’ motion is in
progress, the controller will ignore this command and generate
“MOTION IN PROGRESS” error message. Issue a Stop (ST) or Abort (AB)
motion command to terminate motion initiated by PR""")
get_relative = _make_ask("PR?",
"""This command is used to query the target position of an
axis.""")
set_type = _make_do("QM",
"""Motor type set command.
This command is used to manually set the motor type of an axis.
Send the Motors Check (MC) command to have the controller determine
what motors (if any) are connected. Note that for motor type
‘Tiny’, velocity should not exceed 1750 step/sec. To save the
setting to non-volatile memory, issue the Save (SM) command. Note
that the controller may change this setting if auto motor detection
is enabled by setting bit number 0 in the configuration register to
0 (default) wit ZZ command. When auto motor detection is enabled
the controller checks motor presence and type automatically during
all moves and updates QM status accordingly.""")
get_type = _make_ask("QM?",
"""Get motor type.
This command is used to query the motor type of an axis. It is
important to note that the QM? command simply reports the present
motor type setting in memory. It does not perform a check to
determine whether the setting is still valid or corresponds with
the motor connected at that instant. If motors have been removed
and reconnected to different controller channels or if this is the
first time, connecting this system then issuing the Motor Check
(MC) command is recommended. This will ensure an accurate QM?
command response.""")
position = _make_ask("TP?",
"""Get actual position.
This command is used to query the actual position of an axis. The
actual position represents the internal number of steps made by the
controller relative to its position when controller was powered ON
or a system reset occurred or Home (DH) command was received. Note
that the real or physical position of the actuator/motor may differ
as a function of mechanical precision and inherent open-loop
positioning inaccuracies.""")
set_velocity = _make_do("VA",
"""Set Velocity.
This command is used to set the velocity value for an axis. The
velocity setting specified will not have any effect on a move that
is already in progress. If this command is issued when an axis’
motion is in progress, the controller will accept the new value but
it will use it for subsequent moves only. The maximum velocity for
a ‘Standard’ Picomotor is 2000 steps/sec, while the same for a
‘Tiny’ Picomotor is 1750 steps/sec """)
get_velocity = _make_ask("VA?",
"""Get Velocity.
This command is used to query the velocity value for an axis.""")
stop = _make_do("ST",
"""Stop motion.
This command is used to stop the motion of an axis. The controller
uses acceleration specified using AC command to stop motion. If no
axis number is specified, the controller stops the axis that is
currently moving. Use Abort (AB) command to abruptly stop motion
without deceleration.""")
error_message = _make_ask("TB?",
"""Query error code and the associated message.
The error code is one numerical value up to three(3) digits long.
(see Appendix for complete listing) In general, non-axis specific
errors numbers range from 0- 99. Axis-1 specific errors range from
100-199, Axis-2 errors range from 200-299 and so on. The message is
a description of the error associated with it. All arguments are
separated by commas. Note: Errors are maintained in a FIFO buffer
ten(10) elements deep. When an error is read using TB or TE, the
controller returns the last error that occurred and the error
buffer is cleared by one(1) element. This means that an error can
be read only once, with either command.""", conv=str)
error_code = _make_ask("TE?",
"""Get Error code.
This command is used to read the error code. The error code is one
numerical value up to three(3) digits long. (see Appendix for
complete listing) In general, non-axis specific errors numbers
range from 0-99. Axis-1 specific errors range from 100-199, Axis-2
errors range from 200-299 and so on. Note: Errors are maintained in
a FIFO buffer ten(10) elements deep. When an error is read using TB
or TE, the controller returns the last error that occurred and the
error buffer is cleared by one(1) element. This means that an error
can be read only once, with either command.""")
async def finish(self, xx=None):
while not await self.done(xx):
await asyncio.sleep(self.poll_interval)
async def ping(self):
try:
await self.ask("VE?")
except asyncio.CancelledError:
raise
except:
logger.warning("ping failed", exc_info=True)
return False
return True