plesty.lib.device.base_apt_device ================================= .. py:module:: plesty.lib.device.base_apt_device .. autoapi-nested-parse:: Base device for Thorlabs motion controllers speaking the APT protocol. :class:`BaseAptDevice` wires the Plesty device model to :class:`plesty.lib.traffic.apt.AptTrafficManager`. It provides the standard lifecycle (init/connect/disconnect), identity and error reporting from the APT status word, and single-channel motor operations in raw device counts. Concrete device classes (e.g. a K10CR1 rotation stage) subclass this, define the count-per-unit scale factors of their hardware, and register their configuration parameters against the count-domain helpers via :meth:`register_apt_param`. Attributes ---------- .. autoapisummary:: plesty.lib.device.base_apt_device._COMPLETION_FALLBACK_S Classes ------- .. autoapisummary:: plesty.lib.device.base_apt_device.BaseAptDevice Functions --------- .. autoapisummary:: plesty.lib.device.base_apt_device._descriptor_serial Module Contents --------------- .. py:data:: _COMPLETION_FALLBACK_S :value: 10.0 .. py:class:: BaseAptDevice(port: str, dest: plesty.lib.traffic.apt_protocol.AptAxis = AptAxis.GENERIC, channel: int = 1, timeout: int = 5, move_timeout: int = 60, op_schema: Any = None, param_schema: Any = None, transport: Any | None = None) Bases: :py:obj:`plesty.lib.device.base_device_sync.BaseDeviceSyncModel` Base class for single-channel Thorlabs APT motion devices. Subclasses register their parameter keys with :meth:`register_apt_param` so the generic config system can read and write them, and use the count-domain motion helpers (:meth:`home`, :meth:`move_absolute_counts`, ...) to implement user-facing operations in physical units. Initialize with the serial port and APT addressing. :param port: Serial port of the controller (e.g. 'COM3' or '/dev/ttyUSB0'). :param dest: APT destination address of the controller. :param channel: Motor channel number (APT channels are 1-based). :param timeout: Reply timeout for parameter traffic in seconds. :param move_timeout: Timeout for homing and move completion in seconds. :param op_schema: Optional operation schema forwarded to the function system. :param param_schema: Optional parameter schema forwarded to the config system. :param transport: Optional serial-like object (e.g. :class:`plesty.lib.sim.apt.AptMotorSimulator`) replacing the real serial port — used for hardware-free operation and tests. .. py:attribute:: port .. py:attribute:: dest .. py:attribute:: channel :value: 1 .. py:attribute:: timeout :value: 5 .. py:attribute:: move_timeout :value: 60 .. py:attribute:: _transport :value: None .. py:attribute:: traffic_manager :type: plesty.lib.traffic.apt.AptTrafficManager | None :value: None .. py:attribute:: _param_handlers :type: dict[str, tuple[collections.abc.Callable[[], Any], collections.abc.Callable[[Any], None] | None]] .. py:method:: init(main: Any = None) -> None Create the APT traffic manager for the configured port. .. py:method:: connect() -> bool Open the serial connection and put the controller into remote mode. The configured address may be a serial number rather than a port name, in which case it is resolved first: a COM number is assigned per USB port and moves when a controller is replugged, so an address that survives that has to identify the controller itself. .. py:method:: _start_session() -> bool Open the port and bring the controller into a known state. :returns: ``True`` once the controller is ready for commands. .. py:method:: disconnect() -> None Close the serial connection. .. py:method:: command(message_id: plesty.lib.traffic.apt_protocol.AptMessageId, payload: bytes | None = None) -> None Send a fire-and-forget message to the controller. :param message_id: The message to send. :param payload: Long-packet payload; when omitted, a short packet with the channel number as its first parameter byte is sent. .. py:method:: request(message_id: plesty.lib.traffic.apt_protocol.AptMessageId, reply_id: plesty.lib.traffic.apt_protocol.AptMessageId, payload: bytes | None = None, timeout: float | None = None) -> plesty.lib.traffic.apt_protocol.AptMessage Send a message and return the matching reply. :param message_id: The request message to send. :param reply_id: The expected reply message id. :param payload: Long-packet payload; when omitted, a short packet with the channel number as its first parameter byte is sent. :param timeout: Reply timeout in seconds; defaults to the traffic timeout. :returns: The reply message. .. py:method:: _build(message_id: plesty.lib.traffic.apt_protocol.AptMessageId, payload: bytes | None) -> plesty.lib.traffic.apt_protocol.AptMessage Build a short (channel-addressed) or long message for this device. .. py:method:: hw_info() -> plesty.lib.traffic.apt_protocol.HwInfo Query the controller hardware information block. .. py:method:: identity() -> str Return an identity string built from the APT hardware info. Two serial numbers exist and they need not agree: the one in the USB descriptor, which is how the controller is addressed and what is printed on the unit, and the one the firmware reports in ``HW_GET_INFO``. On the K10CR1 here the descriptor gives 55001327 while the firmware gives 55000000, which looks like an unprogrammed default. Reporting only the firmware value would name the device something no one can find it by, so both appear when they differ. :returns: Model, serial number(s) and firmware version. .. py:method:: get_status() -> plesty.lib.traffic.apt_protocol.Status Query the motor status bits. .. py:method:: check_errors() -> list[str] Return the names of all active error bits in the status word. .. py:method:: check_operatability() -> bool Return True if the controller answers a hardware info request. .. py:property:: is_moving :type: bool Return True while the motor reports any motion status bit. .. py:property:: is_homed :type: bool Return True once the motor has been homed. .. py:method:: _motion_request(message_id: plesty.lib.traffic.apt_protocol.AptMessageId, reply_id: plesty.lib.traffic.apt_protocol.AptMessageId, payload: bytes | None = None, settled: collections.abc.Callable[[], bool] | None = None) -> None Issue a motion command, tolerating a lost completion message. The completion message is a single serial packet and can be lost; the stage state is the ground truth. When the completion wait times out, the motion is accepted anyway once the status bits report it settled — a lost packet must never hang the caller. :param message_id: The motion command to send. :param reply_id: The completion message normally expected. :param payload: Long-packet payload for the command, if any. :param settled: Predicate confirming the motion finished; defaults to ``not self.is_moving``. .. py:method:: home() -> None Home the stage and block until the controller reports completion. .. py:method:: move_absolute_counts(position: int) -> None Move to an absolute position in device counts and block until done. .. py:method:: move_relative_counts(distance: int) -> None Move by a relative distance in device counts and block until done. .. py:method:: stop(immediate: bool = False) -> None Stop any motion and block until the controller reports it stopped. :param immediate: Stop abruptly instead of with a profiled deceleration. .. py:method:: get_position_counts() -> int Return the current position counter in device counts. .. py:method:: get_velocity_params() -> plesty.lib.traffic.apt_protocol.VelParams Query the trapezoidal velocity parameters (in device count units). .. py:method:: set_velocity_params(params: plesty.lib.traffic.apt_protocol.VelParams) -> None Set the trapezoidal velocity parameters (in device count units). .. py:method:: set_params(message_id: plesty.lib.traffic.apt_protocol.AptMessageId, params: plesty.lib.traffic.apt_protocol.AptPayload) -> None Send any ``MOT_SET_*`` parameter structure to the controller. .. py:method:: register_apt_param(key: str, query: collections.abc.Callable[[], Any], write: collections.abc.Callable[[Any], None] | None = None) -> None Bind a configuration key to device read/write callables. :param key: Full configuration key (e.g. ``"Motion.MaxVelocity"``). :param query: Callable returning the current device value for the key. :param write: Callable applying a new value, or ``None`` for read-only. .. py:method:: _resolve_handler_key(key: str) -> str Return the key a handler is registered under, for either spelling. A grouped parameter has two names: the runtime key built from the group's ``command_prefix`` (``MO.MaxVelocity``, which is what ``get_config_list`` reports) and the one built from the group name (``Motion.MaxVelocity``). The config system resolves both, so a module can register a handler under one and be called with the other — and the mismatch fails as "no handler registered" for a parameter that is plainly in the schema. :param key: The key as the caller wrote it. :returns: A key present in the handler map, or *key* unchanged when neither spelling matches. .. py:method:: _write_(key: str, value: str | float | int | bool) -> bool Write a registered parameter to the device. .. py:method:: _query_(key: str) -> Any Query a registered parameter from the device. .. py:function:: _descriptor_serial(manager: Any) -> str | None Return the USB descriptor serial of the port a manager is open on. This is the serial printed on the unit and the one the controller is addressed by, which is not necessarily the one its firmware reports. :param manager: The device's traffic manager. :returns: The serial number, or ``None`` when the port cannot be identified.