# coding: utf-8
from typing import Optional
import logging
from warnings import warn
from .meta_actuator import Actuator
from .._global import OptionalModule
try:
import serial
except (ModuleNotFoundError, ImportError):
serial = OptionalModule("pyserial")
[docs]
class SchneiderMDrive23(Actuator):
"""This class can drive Schneider Electric MDrive 23 stepper motor in speed
and in position.
It communicates with the motor over a serial connection.
.. versionadded:: 1.4.0
.. versionchanged:: 2.0.0 renamed from *CM_drive* to *SchneiderMDrive23*
"""
[docs]
def __init__(self,
port: str = '/dev/ttyUSB0',
baudrate: int = 9600) -> None:
"""Sets the instance attributes and initializes the parent class.
Args:
port: The path to the serial port to open for the serial connection.
baudrate: The baudrate to use for serial communication.
"""
warn(f"Starting from version 2.1.0, {type(self).__name__} will be moved "
f"to crappy.collection. Your code that uses it will still work as "
f"is, except you will now need to import crappy.collection at the "
f"top of your script.", FutureWarning)
self._ser = None
super().__init__()
self._port = port
self._baudrate = baudrate
[docs]
def open(self) -> None:
"""Opens the serial connection to the stepper motor."""
self.log(logging.INFO, f"Opening the serial port {self._port} with "
f"baudrate {self._baudrate}")
self._ser = serial.Serial(self._port, self._baudrate, timeout=0.1)
[docs]
def set_speed(self, speed: float) -> None:
"""Sets the target speed of the stepper motor.
Args:
speed: The target speed to set, in `mm/min`.
"""
# Closing and reopening to get rid of errors
self._ser.close()
self._ser.open()
# Sending the command only if it's below the maximum allowed value
if abs(speed) < 1000000:
self.log(logging.DEBUG, f"Writing b'SL {int(speed)}\\r' to port "
f"{self._port}")
self._ser.write(f'SL {int(speed)}\r')
self._ser.read(self._ser.inWaiting())
else:
self.log(logging.WARNING, "Maximum speed exceeded, not setting speed")
[docs]
def set_position(self, position: float, _: Optional[float]) -> None:
"""Sets the target position for the stepper motor.
Args:
position: The target position to reach, in `mm`.
_: The speed argument is ignored.
.. versionchanged:: 2.0.0 *speed* is now a mandatory argument
.. versionremoved:: 2.0.0 *motion_type* argument
"""
# Closing and reopening to get rid of errors
self._ser.close()
self._ser.open()
# Sending the position command
self.log(logging.DEBUG, f"Writing b'MR {int(position)}\\r' to port "
f"{self._port}")
self._ser.write(f'MR {int(position)}\r')
self._ser.readline()
[docs]
def get_position(self) -> float:
"""Reads, displays and returns the current position in `mm`.
.. versionchanged:: 1.5.2 renamed from *get_pos* to *get_position*
"""
# Closing and reopening to get rid of errors
self._ser.close()
self._ser.open()
# Asking for a position reading
self.log(logging.DEBUG, f"Writing b'PR P \\r' to port {self._port}")
self._ser.write('PR P \r')
pfb = self._ser.readline()
self.log(logging.DEBUG, f"Read {pfb} from port {self._port}")
pfb1 = self._ser.readline()
self.log(logging.DEBUG, f"Read {pfb1} from port {self._port}")
return int(pfb1)
[docs]
def stop(self) -> None:
"""Sends a command for stopping the motor."""
if self._ser is not None:
# Closing and reopening to get rid of errors
self._ser.close()
self._ser.open()
self.log(logging.DEBUG, f"Writing b'SL 0\\r' to port {self._port}")
self._ser.write('SL 0\r')
[docs]
def close(self) -> None:
"""Close the serial connection."""
if self._ser is not None:
self.log(logging.INFO, f"Closing the serial port {self._port}")
self._ser.close()